@operato/scene-table 0.0.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/@types/global/index.d.ts +1 -0
  2. package/CHANGELOG.md +16 -0
  3. package/LICENSE +21 -0
  4. package/README.md +33 -0
  5. package/assets/icon-data-list.png +0 -0
  6. package/assets/icon-table.png +0 -0
  7. package/dist/data-list/data-cell.d.ts +22 -0
  8. package/dist/data-list/data-cell.js +56 -0
  9. package/dist/data-list/data-cell.js.map +1 -0
  10. package/dist/data-list/data-list-layout.d.ts +10 -0
  11. package/dist/data-list/data-list-layout.js +95 -0
  12. package/dist/data-list/data-list-layout.js.map +1 -0
  13. package/dist/data-list/data-list.d.ts +56 -0
  14. package/dist/data-list/data-list.js +536 -0
  15. package/dist/data-list/data-list.js.map +1 -0
  16. package/dist/helper-functions.d.ts +37 -0
  17. package/dist/helper-functions.js +135 -0
  18. package/dist/helper-functions.js.map +1 -0
  19. package/dist/index.d.ts +3 -0
  20. package/dist/index.js +7 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/table-cell.d.ts +41 -0
  23. package/dist/table-cell.js +104 -0
  24. package/dist/table-cell.js.map +1 -0
  25. package/dist/table.d.ts +54 -0
  26. package/dist/table.js +1270 -0
  27. package/dist/table.js.map +1 -0
  28. package/dist/templates/data-list.d.ts +18 -0
  29. package/dist/templates/data-list.js +20 -0
  30. package/dist/templates/data-list.js.map +1 -0
  31. package/dist/templates/index.d.ts +18 -0
  32. package/dist/templates/index.js +4 -0
  33. package/dist/templates/index.js.map +1 -0
  34. package/dist/templates/table.d.ts +20 -0
  35. package/dist/templates/table.js +26 -0
  36. package/dist/templates/table.js.map +1 -0
  37. package/helps/scene/component/data-cell.ko.md +13 -0
  38. package/helps/scene/component/data-cell.md +13 -0
  39. package/helps/scene/component/data-cell.zh.md +13 -0
  40. package/helps/scene/component/data-list.ko.md +6 -0
  41. package/helps/scene/component/data-list.md +6 -0
  42. package/helps/scene/component/data-list.zh.md +8 -0
  43. package/helps/scene/component/table-cell.ko.md +58 -0
  44. package/helps/scene/component/table-cell.md +58 -0
  45. package/helps/scene/component/table-cell.zh.md +61 -0
  46. package/helps/scene/component/table.ko.md +68 -0
  47. package/helps/scene/component/table.md +68 -0
  48. package/helps/scene/component/table.zh.md +68 -0
  49. package/helps/scene/images/table-01.png +0 -0
  50. package/helps/scene/images/table-02.png +0 -0
  51. package/helps/scene/images/table-03.png +0 -0
  52. package/helps/scene/images/table-04.png +0 -0
  53. package/helps/scene/images/table-05.png +0 -0
  54. package/helps/scene/images/table-06.png +0 -0
  55. package/helps/scene/images/table-07.png +0 -0
  56. package/helps/scene/images/table-08.png +0 -0
  57. package/helps/scene/images/table-09.png +0 -0
  58. package/helps/scene/images/table-10.png +0 -0
  59. package/helps/scene/images/table-11.png +0 -0
  60. package/helps/scene/images/table-12.png +0 -0
  61. package/helps/scene/images/table-13.png +0 -0
  62. package/helps/scene/images/table-14.png +0 -0
  63. package/package.json +62 -0
  64. package/src/data-list/data-cell.ts +66 -0
  65. package/src/data-list/data-list-layout.ts +113 -0
  66. package/src/data-list/data-list.ts +654 -0
  67. package/src/helper-functions.ts +168 -0
  68. package/src/index.ts +6 -0
  69. package/src/table-cell.ts +126 -0
  70. package/src/table.ts +1406 -0
  71. package/src/templates/data-list.ts +20 -0
  72. package/src/templates/index.ts +4 -0
  73. package/src/templates/table.ts +26 -0
  74. package/test/basic-test.html +67 -0
  75. package/test/index.html +22 -0
  76. package/test/unit/a-test-table.js +72 -0
  77. package/test/unit/test-table-find-merged-cell.js +95 -0
  78. package/test/unit/test-table-insert-column.js +81 -0
  79. package/test/unit/test-table-insert-row.js +79 -0
  80. package/test/unit/test-table-rows-columns.js +47 -0
  81. package/test/unit/util.js +21 -0
  82. package/things-scene.config.js +5 -0
  83. package/translations/en.json +3 -0
  84. package/translations/ko.json +3 -0
  85. package/translations/ms.json +3 -0
  86. package/translations/zh.json +3 -0
  87. package/tsconfig.json +22 -0
  88. package/tsconfig.tsbuildinfo +1 -0
package/src/table.ts ADDED
@@ -0,0 +1,1406 @@
1
+ /*
2
+ * Copyright © HatioLab Inc. All rights reserved.
3
+ */
4
+
5
+ import {
6
+ CLEAR_STYLE,
7
+ WHERE,
8
+ above,
9
+ after,
10
+ array,
11
+ before,
12
+ below,
13
+ buildCopiedCell,
14
+ buildNewCell,
15
+ columnControlHandler,
16
+ isBottomMost,
17
+ isLeftMost,
18
+ isRightMost,
19
+ isTopMost,
20
+ rowControlHandler,
21
+ setCellBorder
22
+ } from './helper-functions'
23
+ import { Component, ComponentNature, Container, Control, Layout, Model, State, Style } from '@hatiolab/things-scene'
24
+
25
+ import { TableCell } from '.'
26
+
27
+ const NATURE = {
28
+ mutable: false,
29
+ resizable: true,
30
+ rotatable: true,
31
+ properties: [
32
+ {
33
+ type: 'number',
34
+ label: 'rows',
35
+ name: 'rows',
36
+ property: 'rows'
37
+ },
38
+ {
39
+ type: 'number',
40
+ label: 'columns',
41
+ name: 'columns',
42
+ property: 'columns'
43
+ },
44
+ {
45
+ type: 'select',
46
+ label: 'data-spread-to',
47
+ name: 'spreadTo',
48
+ property: {
49
+ options: ['text', 'data']
50
+ }
51
+ }
52
+ ],
53
+ 'value-property': 'data',
54
+ help: 'scene/component/table'
55
+ }
56
+
57
+
58
+ export default class Table extends Container {
59
+
60
+ created() {
61
+ var tobeSize = this.rows * this.columns
62
+ var gap = this.size() - tobeSize
63
+
64
+ if (this.data) {
65
+ this.setCellsData()
66
+ }
67
+
68
+ if (gap == 0) {
69
+ return
70
+ } else if (gap > 0) {
71
+ let removals = this.components.slice(gap)
72
+ this.remove(removals)
73
+ } else {
74
+ let newbies = []
75
+
76
+ for (let i = 0; i < -gap; i++) newbies.push(buildNewCell('table-cell', this.app))
77
+
78
+ this.add(newbies)
79
+ }
80
+
81
+ var widths = this.get('widths')
82
+ var heights = this.get('heights')
83
+
84
+ if (!widths || widths.length < this.columns) this.set('widths', this.widths)
85
+ if (!heights || heights.length < this.rows) this.set('heights', this.heights)
86
+ }
87
+
88
+ containable(component: Component) {
89
+ return component.get('type') == 'table-cell'
90
+ }
91
+
92
+ get focusible() {
93
+ /* 이 컨테이너에는 컴포넌트를 임의로 추가 및 삭제할 수 없도록 함 */
94
+ return false
95
+ }
96
+
97
+ get widths() {
98
+ var widths = this.get('widths')
99
+
100
+ if (!widths) return array(1, this.columns)
101
+
102
+ if (widths.length < this.columns) return widths.concat(array(1, this.columns - widths.length))
103
+ else if (widths.length > this.columns) return widths.slice(0, this.columns)
104
+
105
+ return widths
106
+ }
107
+
108
+ get heights() {
109
+ var heights = this.get('heights')
110
+
111
+ if (!heights) return array(1, this.rows)
112
+
113
+ if (heights.length < this.rows) return heights.concat(array(1, this.rows - heights.length))
114
+ else if (heights.length > this.rows) return heights.slice(0, this.rows)
115
+
116
+ return heights
117
+ }
118
+
119
+ buildCells(newrows: number, newcolumns: number, oldrows: number, oldcolumns: number) {
120
+ if (newrows < oldrows) {
121
+ let removals: TableCell[] = [] = this.components.slice(oldcolumns * newrows) as TableCell[]
122
+
123
+ // 지우려는 셀중에 병합된 셀을 찾는다.
124
+ let mergedCells: TableCell[] = []
125
+ removals.forEach(cell => {
126
+ if (cell.merged === true || cell.rowspan > 1 || cell.colspan > 1) mergedCells.push(cell)
127
+ })
128
+
129
+ // 병합된 셀 중에서 슈퍼셀을 찾는다.
130
+ if (mergedCells.length > 0) {
131
+ // 부모 셀을 저장
132
+ let superCells: TableCell[] = []
133
+ // 부모 셀의 인덱스를 저장
134
+ let superCellIndexes: number[] = []
135
+ mergedCells.forEach(cell => {
136
+ let col, row, index
137
+ col = this.components.indexOf(cell) % oldcolumns
138
+ row = Math.floor(this.components.indexOf(cell) / oldcolumns)
139
+ index = row * oldcolumns + col + 1
140
+ while (index) {
141
+ --index
142
+ let component: TableCell = this.components[index] as TableCell
143
+ // 슈퍼셀을 찾고 슈퍼셀의 위치에서 rowspan, colspan 거리만큼 이동하면서 cell이 있는지 검증해야함
144
+ if (component.rowspan > 1 || component.colspan > 1) {
145
+ let spColStart = this.components.indexOf(component) % oldcolumns
146
+ let spColEnd = (this.components.indexOf(component) % oldcolumns) + component.colspan
147
+ let spRowStart = Math.floor(this.components.indexOf(component) / oldcolumns)
148
+ let spRowEnd = Math.floor(this.components.indexOf(component) / oldcolumns) + component.rowspan
149
+ // 슈퍼셀 영역 안에 자식 셀이 있으면 superCells에 부모셀을 추가
150
+ if (col >= spColStart && col < spColEnd && (row >= spRowStart && row < spRowEnd)) {
151
+ if (-1 == superCellIndexes.indexOf(index)) {
152
+ superCellIndexes.push(index)
153
+ superCells.push(component)
154
+ }
155
+ }
156
+ }
157
+ }
158
+ })
159
+ // 슈퍼셀에서 colspan 을 감소시킨다
160
+ superCells.forEach(cell => {
161
+ // newcolumns < oldcolumns 케이스와 이 부분만 다름
162
+ cell.rowspan -= oldrows - newrows
163
+ })
164
+ }
165
+
166
+ this.remove(removals)
167
+ }
168
+
169
+ var minrows = Math.min(newrows, oldrows)
170
+
171
+ if (newcolumns > oldcolumns) {
172
+ for (let r = 0; r < minrows; r++) {
173
+ for (let c = oldcolumns; c < newcolumns; c++) {
174
+ this.insertComponentAt(buildNewCell('table-cell', this.app), r * newcolumns + c)
175
+ }
176
+ }
177
+ } else if (newcolumns < oldcolumns) {
178
+ let removals: TableCell[] = []
179
+
180
+ for (let r = 0; r < minrows; r++) {
181
+ for (let c = newcolumns; c < oldcolumns; c++) {
182
+ removals.push(this.components[r * oldcolumns + c] as TableCell)
183
+ }
184
+ }
185
+ // 지우려는 셀중에 병합된 셀을 찾는다.
186
+ let mergedCells: TableCell[] = []
187
+ removals.forEach(cell => {
188
+ if (cell.merged === true || cell.rowspan > 1 || cell.colspan > 1) mergedCells.push(cell)
189
+ })
190
+
191
+ // 병합된 셀 중에서 슈퍼셀을 찾는다.
192
+ if (mergedCells.length > 0) {
193
+ // 부모 셀을 저장
194
+ let superCells: TableCell[] = []
195
+ // 부모 셀의 인덱스를 저장
196
+ let superCellIndexes: number[] = []
197
+ mergedCells.forEach(cell => {
198
+ let col, row, index
199
+ col = this.components.indexOf(cell) % oldcolumns
200
+ row = Math.floor(this.components.indexOf(cell) / oldcolumns)
201
+ index = row * oldcolumns + col + 1
202
+ while (index) {
203
+ --index
204
+ let component = this.components[index] as TableCell
205
+ // 슈퍼셀을 찾고 슈퍼셀의 위치에서 rowspan, colspan 거리만큼 이동하면서 cell이 있는지 검증해야함
206
+ if (component.rowspan > 1 || component.colspan > 1) {
207
+ let spColStart = this.components.indexOf(component) % oldcolumns
208
+ let spColEnd = (this.components.indexOf(component) % oldcolumns) + component.colspan
209
+ let spRowStart = Math.floor(this.components.indexOf(component) / oldcolumns)
210
+ let spRowEnd = Math.floor(this.components.indexOf(component) / oldcolumns) + component.rowspan
211
+ // 슈퍼셀 영역 안에 자식 셀이 있으면 superCells에 부모셀을 추가
212
+ if (col >= spColStart && col < spColEnd && (row >= spRowStart && row < spRowEnd)) {
213
+ if (-1 == superCellIndexes.indexOf(index)) {
214
+ superCellIndexes.push(index)
215
+ superCells.push(component)
216
+ }
217
+ }
218
+ }
219
+ }
220
+ })
221
+ // 슈퍼셀에서 colspan 을 감소시킨다
222
+ superCells.forEach(cell => {
223
+ cell.colspan -= oldcolumns - newcolumns
224
+ })
225
+ }
226
+
227
+ this.remove(removals)
228
+ }
229
+
230
+ if (newrows > oldrows) {
231
+ let newbies = []
232
+
233
+ for (let r = oldrows; r < newrows; r++) {
234
+ for (let i = 0; i < newcolumns; i++) {
235
+ newbies.push(buildNewCell('table-cell', this.app))
236
+ }
237
+ }
238
+ this.add(newbies)
239
+ }
240
+
241
+ this.set({
242
+ widths: this.widths,
243
+ heights: this.heights
244
+ })
245
+ }
246
+
247
+ get layout() {
248
+ return Layout.get('table')
249
+ }
250
+
251
+ get rows() {
252
+ return Number(this.get('rows'))
253
+ }
254
+
255
+ setCellsStyle(cells: TableCell[], style: Style, where: WHERE) {
256
+ var components = this.components
257
+ var total = components.length
258
+ var columns = this.get('columns')
259
+
260
+ // 병합된 셀도 포함시킨다.
261
+ var _cells: TableCell[] = []
262
+ cells.forEach(c => {
263
+ _cells.push(c)
264
+ if (c.colspan || c.rowspan) {
265
+ let col = this.getRowColumn(c).column
266
+ let row = this.getRowColumn(c).row
267
+ for (let i = row; i < row + c.rowspan; i++)
268
+ for (let j = col; j < col + c.colspan; j++)
269
+ if (i != row || j != col) _cells.push(this.components[i * this.columns + j] as TableCell)
270
+ }
271
+ })
272
+ var indices = _cells.map(cell => components.indexOf(cell))
273
+ indices.forEach(i => {
274
+ var cell = components[i]
275
+
276
+ switch (where) {
277
+ case 'all':
278
+ setCellBorder(cell, style, where)
279
+
280
+ if (isLeftMost(total, columns, indices, i)) setCellBorder(components[before(columns, i)], style, 'right')
281
+ if (isRightMost(total, columns, indices, i)) setCellBorder(components[after(columns, i)], style, 'left')
282
+ if (isTopMost(total, columns, indices, i)) setCellBorder(components[above(columns, i)], style, 'bottom')
283
+ if (isBottomMost(total, columns, indices, i)) setCellBorder(components[below(columns, i)], style, 'top')
284
+ break
285
+ case 'in':
286
+ if (!isLeftMost(total, columns, indices, i)) {
287
+ setCellBorder(cell, style, 'left')
288
+ }
289
+ if (!isRightMost(total, columns, indices, i)) {
290
+ setCellBorder(cell, style, 'right')
291
+ }
292
+ if (!isTopMost(total, columns, indices, i)) {
293
+ setCellBorder(cell, style, 'top')
294
+ }
295
+ if (!isBottomMost(total, columns, indices, i)) {
296
+ setCellBorder(cell, style, 'bottom')
297
+ }
298
+ break
299
+ case 'out':
300
+ if (isLeftMost(total, columns, indices, i)) {
301
+ setCellBorder(cell, style, 'left')
302
+ setCellBorder(components[before(columns, i)], style, 'right')
303
+ }
304
+ if (isRightMost(total, columns, indices, i)) {
305
+ setCellBorder(cell, style, 'right')
306
+ setCellBorder(components[after(columns, i)], style, 'left')
307
+ }
308
+ if (isTopMost(total, columns, indices, i)) {
309
+ setCellBorder(cell, style, 'top')
310
+ setCellBorder(components[above(columns, i)], style, 'bottom')
311
+ }
312
+ if (isBottomMost(total, columns, indices, i)) {
313
+ setCellBorder(cell, style, 'bottom')
314
+ setCellBorder(components[below(columns, i)], style, 'top')
315
+ }
316
+ break
317
+ case 'left':
318
+ if (isLeftMost(total, columns, indices, i)) {
319
+ setCellBorder(cell, style, 'left')
320
+ setCellBorder(components[before(columns, i)], style, 'right')
321
+ }
322
+ break
323
+ case 'right':
324
+ if (isRightMost(total, columns, indices, i)) {
325
+ setCellBorder(cell, style, 'right')
326
+ setCellBorder(components[after(columns, i)], style, 'left')
327
+ }
328
+ break
329
+ case 'center':
330
+ if (!isLeftMost(total, columns, indices, i)) {
331
+ setCellBorder(cell, style, 'left')
332
+ }
333
+ if (!isRightMost(total, columns, indices, i)) {
334
+ setCellBorder(cell, style, 'right')
335
+ }
336
+ break
337
+ case 'middle':
338
+ if (!isTopMost(total, columns, indices, i)) {
339
+ setCellBorder(cell, style, 'top')
340
+ }
341
+ if (!isBottomMost(total, columns, indices, i)) {
342
+ setCellBorder(cell, style, 'bottom')
343
+ }
344
+ break
345
+ case 'top':
346
+ if (isTopMost(total, columns, indices, i)) {
347
+ setCellBorder(cell, style, 'top')
348
+ setCellBorder(components[above(columns, i)], style, 'bottom')
349
+ }
350
+ break
351
+ case 'bottom':
352
+ if (isBottomMost(total, columns, indices, i)) {
353
+ setCellBorder(cell, style, 'bottom')
354
+ setCellBorder(components[below(columns, i)], style, 'top')
355
+ }
356
+ break
357
+ case 'clear':
358
+ setCellBorder(cell, CLEAR_STYLE, 'all')
359
+
360
+ if (isLeftMost(total, columns, indices, i))
361
+ setCellBorder(components[before(columns, i)], CLEAR_STYLE, 'right')
362
+ if (isRightMost(total, columns, indices, i)) setCellBorder(components[after(columns, i)], CLEAR_STYLE, 'left')
363
+ if (isTopMost(total, columns, indices, i)) setCellBorder(components[above(columns, i)], CLEAR_STYLE, 'bottom')
364
+ if (isBottomMost(total, columns, indices, i)) setCellBorder(components[below(columns, i)], CLEAR_STYLE, 'top')
365
+ }
366
+ })
367
+ }
368
+
369
+ setCellsData() {
370
+ var data = this.data
371
+
372
+ if (!data) return
373
+
374
+ data = this.toObjectArrayValue(data) || []
375
+
376
+ var cells = this.components as TableCell[]
377
+
378
+ var { spreadTo = 'text' } = this.state
379
+
380
+ cells.forEach(cell => {
381
+ var dataKey = cell.model.dataKey
382
+ var dataIndex = cell.model.dataIndex
383
+ if (dataKey && dataIndex >= 0) {
384
+ (cell as any)[spreadTo] = (data[dataIndex] || {})[dataKey]
385
+ }
386
+ })
387
+ }
388
+
389
+ getRowColumn(cell: TableCell): {column: number, row: number} {
390
+ var idx = this.components.indexOf(cell)
391
+
392
+ return {
393
+ column: idx % this.columns,
394
+ row: Math.floor(idx / this.columns)
395
+ }
396
+ }
397
+
398
+ getCellsByRow(row: number) {
399
+ return this.components.slice(row * this.columns, (row + 1) * this.columns)
400
+ }
401
+
402
+ getCellsByColumn(column: number) {
403
+ var cells = []
404
+ for (var i = 0; i < this.rows; i++) cells.push(this.components[this.columns * i + column])
405
+
406
+ return cells
407
+ }
408
+
409
+ // 한 개의 행을 매개변수로 받아서 첫 번째 셀부터 우측으로 이동하면서 병합된 셀이 있는지 검사한다.
410
+ findMergedCellByX(row: number) {
411
+ let mergedCells: TableCell[] = []
412
+ let cell: TableCell
413
+ for (let i = 0; i < this.columns; i++) {
414
+ cell = this.components[row * this.columns + i] as TableCell
415
+ if (cell.merged === true || cell.rowspan > 1 || cell.colspan > 1) mergedCells.push(cell)
416
+ }
417
+ return mergedCells
418
+ }
419
+
420
+ // 한 개의 열을 매개변수로 받아서 첫 번째 셀부터 아래로 이동하면서 병합된 셀이 있는지 검사한다.
421
+ findMergedCellByY(column: number) {
422
+ let mergedCells: TableCell[] = []
423
+ let cell: TableCell
424
+ for (let i = 0; i < this.rows; i++) {
425
+ cell = this.components[i * this.columns + column] as TableCell
426
+ if (cell.merged === true || cell.rowspan > 1 || cell.colspan > 1) mergedCells.push(cell)
427
+ }
428
+ return mergedCells
429
+ }
430
+
431
+ mergeCells(cells: TableCell[]) {
432
+ // 선택한 셀이 들어있는 행
433
+ let mergeableRows: number[] = []
434
+ cells.forEach(cell => {
435
+ let row = this.getRowColumn(cell).row
436
+ if (-1 == mergeableRows.indexOf(row)) mergeableRows.push(row)
437
+ })
438
+
439
+ // 선택한 셀의 행이 연속적인 숫자가 아니라면 병합하지 않는다.
440
+ if (mergeableRows.length - 1 !== mergeableRows[mergeableRows.length - 1] - mergeableRows[0]) return false
441
+
442
+ // 선택한 셀이 들어있는 열
443
+ let mergeableColumns: number[] = []
444
+ cells.forEach(cell => {
445
+ let column = this.getRowColumn(cell).column
446
+ if (-1 == mergeableColumns.indexOf(column)) mergeableColumns.push(column)
447
+ })
448
+
449
+ // 선택한 셀의 열이 연속적인 숫자가 아니라면 병합하지 않는다.
450
+ if (mergeableColumns.length - 1 !== mergeableColumns[mergeableColumns.length - 1] - mergeableColumns[0])
451
+ return false
452
+
453
+ // 병합할 행의 수
454
+ let numberOfRows = mergeableRows.length
455
+
456
+ // 병합할 열의 수
457
+ let numberOfColumns = mergeableColumns.length
458
+
459
+ // 선택된 셀의 수
460
+ let numberOfCells = cells.length
461
+
462
+ // 병합될 조건 검사
463
+ // 행과 열의 곱이 셀의 수가 아니거나 셀의 수가 2보다 작은 경우는 병합하지 않는다.
464
+ if (numberOfCells !== numberOfRows * numberOfColumns || numberOfCells < 2) return false
465
+
466
+ // 선택한 셀들을 index 값이 낮은 것부터 순서대로 재정렬
467
+ cells.sort((a, b) => {
468
+ return (
469
+ this.getRowColumn(a).row * this.columns +
470
+ this.getRowColumn(a).column -
471
+ (this.getRowColumn(b).row * this.columns + this.getRowColumn(b).column)
472
+ )
473
+ })
474
+
475
+ // 셀을 병합함
476
+ let firstCell = cells[0]
477
+ firstCell.set({
478
+ colspan: numberOfColumns,
479
+ rowspan: numberOfRows
480
+ })
481
+
482
+ // 첫 번째 셀을 제외한 나머지 셀을 true로 지정
483
+ for (let i = 1; i < numberOfCells; i++) cells[i].merged = true
484
+
485
+ // 병합 후에는 첫 번째 셀을 선택하도록 함
486
+ this.root.selected = [firstCell]
487
+ }
488
+
489
+ splitCells(cells: TableCell[]) {
490
+ // 선택한 병합된 셀의 정보를 가져온다.
491
+ let firstCellRowColumn = this.getRowColumn(cells[0])
492
+ let firstCell = cells[0]
493
+ let firstCellIndex = this.components.indexOf(cells[0])
494
+ let length = this.components.length
495
+ let lastCell = this.components[length - 1] as TableCell
496
+ let lastCellRowColumn = this.getRowColumn(lastCell)
497
+ let startIndex = length / (lastCellRowColumn.row + 1)
498
+
499
+ // 병합된 셀들을 구해서 merged를 false로 설정한다.
500
+ // 자식 셀이 갖고 있는 부모 셀의 위치를 초기화 한다.
501
+ for (let j = 0; j < firstCell.rowspan; j++) {
502
+ let index
503
+ let nextCell: TableCell
504
+ for (let i = firstCellIndex; i < firstCellIndex + firstCell.colspan; i++) {
505
+ index = startIndex * j + i
506
+ nextCell = this.components[index] as TableCell
507
+ nextCell.merged = false
508
+ }
509
+ }
510
+
511
+ // 첫 번째 셀의 rowspan, colspan = 1로 지정한다.
512
+ firstCell.colspan = 1
513
+ firstCell.rowspan = 1
514
+ }
515
+
516
+ deleteRows(cells: TableCell[]) {
517
+ // 만약 선택한 셀이 병합된 셀이라면 삭제하지 않는다.
518
+ if (cells[0].merged == true) return false
519
+ // 먼저 cells 위치의 행을 구한다.
520
+ let rows: number[] = []
521
+ cells.forEach(cell => {
522
+ let row = this.getRowColumn(cell).row
523
+ if (-1 == rows.indexOf(row)) rows.push(row)
524
+ })
525
+ rows.sort((a, b) => {
526
+ return a - b
527
+ })
528
+ rows.reverse()
529
+ var heights = this.heights.slice()
530
+ rows.forEach(row => {
531
+ // rows에서 가로 방향으로 이동하면서 병합된 셀을 찾는다.
532
+ let mergedCells = this.findMergedCellByX(row)
533
+ // mergedCells.length가 0이면 일반적으로 행을 지운다.
534
+ if (mergedCells.length === 0) {
535
+ this.remove(this.getCellsByRow(row))
536
+ }
537
+ // mergedCells.length가 0이 아니면 병합된 셀을 고려하여 행을 지워야 한다.
538
+ //
539
+ else {
540
+ // 삭제할 행에서 병합된 셀을 삭제할 때 해당 셀을 임시로 저장
541
+ let temp = []
542
+ // 부모 셀을 저장
543
+ let superCells = []
544
+ // 부모 셀의 인덱스 값을 저장
545
+ let superCellIndexes: number[] = []
546
+ mergedCells.forEach(cell => {
547
+ let col, row, index
548
+ col = this.getRowColumn(cell).column
549
+ row = this.getRowColumn(cell).row
550
+ index = row * this.columns + col + 1
551
+ while (index) {
552
+ --index
553
+ let component = this.components[index] as TableCell
554
+ // 슈퍼셀을 찾고 슈퍼셀의 위치에서 rowspan, colspan 거리만큼 이동하면서 cell이 있는지 검증해야함
555
+ if (component.rowspan > 1 || component.colspan > 1) {
556
+ let spColStart = this.getRowColumn(component).column
557
+ let spColEnd = this.getRowColumn(component).column + component.colspan
558
+ let spRowStart = this.getRowColumn(component).row
559
+ let spRowEnd = this.getRowColumn(component).row + component.rowspan
560
+ // 슈퍼셀 영역 안에 자식 셀이 있으면 superCells에 부모셀을 추가
561
+ if (col >= spColStart && col < spColEnd && (row >= spRowStart && row < spRowEnd)) {
562
+ if (-1 == superCellIndexes.indexOf(index)) {
563
+ superCellIndexes.push(index)
564
+ superCells.push(component)
565
+ }
566
+ }
567
+ }
568
+ }
569
+ })
570
+ superCellIndexes.forEach(index => {
571
+ let superCellRow = Math.floor(index / this.columns)
572
+ // 지우려는 행이 슈퍼셀을 포함한 경우이면서 슈퍼셀이 마지막 행의 셀이 아닌 경우
573
+ // 그리고 슈퍼셀의 rowspan이 1보다 큰 경우
574
+ let aCell: TableCell = this.components[index + this.columns] as TableCell
575
+ let bCell: TableCell = this.components[index] as TableCell
576
+ if (row === superCellRow && superCellRow !== this.rows - 1 && bCell.rowspan > 1) {
577
+ aCell.rowspan = bCell.rowspan - 1
578
+ aCell.colspan = bCell.colspan
579
+ aCell.merged = false
580
+ aCell.set('text', bCell.get('text'))
581
+ } else {
582
+ bCell.rowspan -= 1
583
+ }
584
+ })
585
+ this.remove(this.getCellsByRow(row))
586
+ }
587
+ })
588
+ heights.splice(rows, 1)
589
+ this.model.rows -= rows.length // 고의적으로, change 이벤트가 발생하지 않도록 set(..)을 사용하지 않음.
590
+ this.set('heights', heights)
591
+ }
592
+
593
+ deleteColumns(cells: TableCell[]) {
594
+ // 만약 선택한 셀이 병합된 셀이라면 삭제하지 않는다.
595
+ if (cells[0].merged == true) return false
596
+ // 먼저 cells 위치의 열을 구한다.
597
+ let columns: number[] = []
598
+ cells.forEach(cell => {
599
+ let column = this.getRowColumn(cell).column
600
+ if (-1 == columns.indexOf(column)) columns.push(column)
601
+ })
602
+ columns.sort((a, b) => {
603
+ return a - b
604
+ })
605
+ columns.reverse()
606
+
607
+ columns.forEach(column => {
608
+ var widths = this.widths.slice()
609
+ // columns에서 세로 방향으로 이동하면서 병합된 셀을 찾는다.
610
+ let mergedCells = this.findMergedCellByY(column)
611
+ // mergedCells.length가 0이면 일반적으로 열을 지운다.
612
+ if (mergedCells.length === 0) {
613
+ this.remove(this.getCellsByColumn(column))
614
+ }
615
+ // mergedCells.length가 0이 아니면 병합된 셀을 고려하여 열을 지워야 한다.
616
+ else {
617
+ // 삭제할 열에서 병합된 셀을 삭제할 때 해당 셀을 임시로 저장
618
+ let temp = []
619
+ // 부모 셀을 저장
620
+ let superCells = []
621
+ // 부모 셀의 인덱스를 저장
622
+ let superCellIndexes: number[] = []
623
+ mergedCells.forEach(cell => {
624
+ let col, row, index
625
+ col = this.getRowColumn(cell).column
626
+ row = this.getRowColumn(cell).row
627
+ index = row * this.columns + col + 1
628
+ while (index) {
629
+ --index
630
+ let component = this.components[index] as TableCell
631
+ // 슈퍼셀을 찾고 슈퍼셀의 위치에서 rowspan, colspan 거리만큼 이동하면서 cell이 있는지 검증해야함
632
+ if (component.rowspan > 1 || component.colspan > 1) {
633
+ let spColStart = this.getRowColumn(component).column
634
+ let spColEnd = this.getRowColumn(component).column + component.colspan
635
+ let spRowStart = this.getRowColumn(component).row
636
+ let spRowEnd = this.getRowColumn(component).row + component.rowspan
637
+ // 슈퍼셀 영역 안에 자식 셀이 있으면 superCells에 부모셀을 추가
638
+ if (col >= spColStart && col < spColEnd && (row >= spRowStart && row < spRowEnd)) {
639
+ if (-1 == superCellIndexes.indexOf(index)) {
640
+ superCellIndexes.push(index)
641
+ superCells.push(component)
642
+ }
643
+ }
644
+ }
645
+ }
646
+ })
647
+ superCellIndexes.forEach(index => {
648
+ let superCellColumn = index % this.columns
649
+
650
+ let aCell: TableCell = this.components[index + 1] as TableCell
651
+ let bCell: TableCell = this.components[index] as TableCell
652
+ // 지우려는 열이 슈퍼셀을 포함한 경우이면서 슈퍼셀이 마지막 열의 셀이 아닌 경우
653
+ // 그리고 슈퍼셀의 colspan이 1보다 큰 경우
654
+ if (
655
+ column === superCellColumn &&
656
+ superCellColumn !== this.columns - 1 &&
657
+ bCell.colspan > 1
658
+ ) {
659
+ aCell.rowspan = bCell.rowspan
660
+ aCell.colspan = bCell.colspan - 1
661
+ aCell.merged = false
662
+ aCell.set('text', bCell.get('text'))
663
+ } else {
664
+ bCell.colspan -= 1
665
+ }
666
+ })
667
+ this.remove(this.getCellsByColumn(column))
668
+ }
669
+ widths.splice(column, 1)
670
+ this.model.columns -= 1 // 고의적으로, change 이벤트가 발생하지 않도록 set(..)을 사용하지 않음.
671
+ this.set('widths', widths)
672
+ })
673
+ }
674
+
675
+ insertCellsAbove(cells: TableCell[]) {
676
+ // 먼저 cells 위치의 행을 구한다.
677
+ let rows: number[] = []
678
+ cells.forEach(cell => {
679
+ let row = this.getRowColumn(cell).row
680
+ if (-1 == rows.indexOf(row)) rows.push(row)
681
+ })
682
+ rows.sort((a, b) => {
683
+ return a - b
684
+ })
685
+ rows.reverse()
686
+ // 행 2개 이상은 추가 안함. 임시로 막아놓음
687
+ if (rows.length >= 2) return false
688
+ let insertionRowPosition = rows[0]
689
+ let newbieRowHeights: number[] = []
690
+ let newbieCells: TableCell[] = []
691
+ rows.forEach(row => {
692
+ // rows에서 가로 방향으로 이동하면서 병합된 셀을 찾는다.
693
+ let mergedCells = this.findMergedCellByX(row)
694
+ // mergedCells.length가 0이면 일반적으로 행을 위에 추가한다.
695
+ if (mergedCells.length === 0) {
696
+ for (let i = 0; i < this.columns; i++)
697
+ newbieCells.push(buildCopiedCell(this.components[row * this.columns + i].model, this.app))
698
+ newbieRowHeights.push(this.heights[row])
699
+
700
+ newbieCells.reverse().forEach(cell => {
701
+ this.insertComponentAt(cell, insertionRowPosition * this.columns)
702
+ })
703
+
704
+ let heights = this.heights.slice()
705
+ heights.splice(insertionRowPosition, 0, ...newbieRowHeights)
706
+ this.set('heights', heights)
707
+
708
+ this.model.rows += rows.length
709
+
710
+ this.clearCache()
711
+ }
712
+ // mergedCells.length가 0이 아니면 병합된 셀을 고려하여 행을 추가해야 한다.
713
+ else {
714
+ // 선택한 행이 2개 이상 있고 그 중에 병합된 셀이 적어도 한 개라도 있으면
715
+ // 병합된 셀이 포함된 행의 추가는 무시한다. 임시방편으로 막아놈
716
+ if (rows.length > 1) return false
717
+ // 추가할 행에서 병합된 셀을 추가할 때 해당 셀을 임시로 저장
718
+ let temp = []
719
+ // 부모 셀을 저장
720
+ let superCells = []
721
+ // 부모 셀의 인덱스 값을 저장
722
+ let superCellIndexes: number[] = []
723
+ mergedCells.forEach(cell => {
724
+ let col, row, index
725
+ col = this.getRowColumn(cell).column
726
+ row = this.getRowColumn(cell).row
727
+ index = row * this.columns + col + 1
728
+ while (index) {
729
+ --index
730
+ let component = this.components[index] as TableCell
731
+ // 슈퍼셀을 찾고 슈퍼셀의 위치에서 rowspan, colspan 거리만큼 이동하면서 cell이 있는지 검증해야함
732
+ if (component.rowspan > 1 || component.colspan > 1) {
733
+ let spColStart = this.getRowColumn(component).column
734
+ let spColEnd = this.getRowColumn(component).column + component.colspan
735
+ let spRowStart = this.getRowColumn(component).row
736
+ let spRowEnd = this.getRowColumn(component).row + component.rowspan
737
+ // 슈퍼셀 영역 안에 자식 셀이 있으면 superCells에 부모셀을 추가
738
+ if (col >= spColStart && col < spColEnd && (row >= spRowStart && row < spRowEnd)) {
739
+ if (-1 == superCellIndexes.indexOf(index)) {
740
+ superCellIndexes.push(index)
741
+ superCells.push(component)
742
+ }
743
+ }
744
+ }
745
+ }
746
+ })
747
+ superCellIndexes.forEach(index => {
748
+ // 추가하려는 셀은 일반 셀인데 그 위치에 다른 병합된 셀이 있는 문제로 임시로 막아 놓음. 수정해야함
749
+ if (superCellIndexes.length >= 2) return false
750
+ let superCellRow = Math.floor(index / this.columns)
751
+ let cell: TableCell = this.components[index] as TableCell
752
+
753
+ let superCellObj = {
754
+ rowspan: cell.rowspan,
755
+ colspan: cell.colspan,
756
+ text: cell.get('text'),
757
+ merged: cell.merged
758
+ }
759
+
760
+ // 추가하려는 행이 슈퍼셀을 포함한 경우
761
+ if (superCellRow === row) {
762
+ for (let i = 0; i < this.columns; i++) newbieCells.push(buildNewCell('table-cell', this.app))
763
+ newbieRowHeights.push(this.heights[row])
764
+
765
+ newbieCells.reverse().forEach(cell => {
766
+ this.insertComponentAt(cell, insertionRowPosition * this.columns)
767
+ })
768
+ let addedCell = this.components[index + this.columns] as TableCell
769
+
770
+ addedCell.rowspan = superCellObj.rowspan
771
+ addedCell.colspan = superCellObj.colspan
772
+ addedCell.set('text', superCellObj.text)
773
+ addedCell.merged = superCellObj.merged
774
+ } else {
775
+ for (let i = 0; i < this.columns; i++)
776
+ newbieCells.push(buildCopiedCell(this.components[row * this.columns + i].model, this.app))
777
+ newbieRowHeights.push(this.heights[row])
778
+
779
+ newbieCells.reverse().forEach(cell => {
780
+ this.insertComponentAt(cell, insertionRowPosition * this.columns)
781
+ })
782
+ cell.rowspan += 1
783
+ }
784
+ let heights = this.heights.slice()
785
+ heights.splice(insertionRowPosition, 0, ...newbieRowHeights)
786
+ this.set('heights', heights)
787
+
788
+ this.model.rows += rows.length
789
+
790
+ this.clearCache()
791
+ })
792
+ }
793
+ })
794
+ }
795
+
796
+ insertCellsBelow(cells: TableCell[]) {
797
+ // 먼저 cells 위치의 행을 구한다.
798
+ let rows: number[] = []
799
+ cells.forEach(cell => {
800
+ let row = this.getRowColumn(cell).row
801
+ if (-1 == rows.indexOf(row)) rows.push(row)
802
+ })
803
+ rows.sort((a, b) => {
804
+ return a - b
805
+ })
806
+ rows.reverse()
807
+
808
+ // 행 2개 이상은 추가 안함. 임시로 막아놓음
809
+ if (rows.length >= 2) return false
810
+ let insertionRowPosition = rows[rows.length - 1] + 1
811
+ let newbieRowHeights: number[] = []
812
+ let newbieCells: TableCell[] = []
813
+
814
+ rows.forEach(row => {
815
+ // rows에서 가로 방향으로 이동하면서 병합된 셀을 찾는다.
816
+ let mergedCells = this.findMergedCellByX(row)
817
+ // mergedCells.length가 0이면 일반적으로 행을 아래에 추가한다.
818
+ if (mergedCells.length === 0) {
819
+ for (let i = 0; i < this.columns; i++)
820
+ newbieCells.push(buildCopiedCell(this.components[row * this.columns + i].model, this.app))
821
+ newbieRowHeights.push(this.heights[row])
822
+
823
+ newbieCells.reverse().forEach(cell => {
824
+ this.insertComponentAt(cell, insertionRowPosition * this.columns)
825
+ })
826
+
827
+ let heights = this.heights.slice()
828
+ heights.splice(insertionRowPosition, 0, ...newbieRowHeights)
829
+ this.set('heights', heights)
830
+
831
+ this.model.rows += 1
832
+
833
+ this.clearCache()
834
+ }
835
+ // mergedCells.length가 0이 아니면 병합된 셀을 고려하여 행을 추가해야 한다.
836
+ else {
837
+ // 추가할 행에서 병합된 셀을 추가할 때 해당 셀을 임시로 저장
838
+ let temp = []
839
+ // 부모 셀을 저장
840
+ let superCells: TableCell[] = []
841
+ // 부모 셀의 인덱스 값을 저장
842
+ let superCellIndexes: number[] = []
843
+ mergedCells.forEach(cell => {
844
+ let col, row, index
845
+ col = this.getRowColumn(cell).column
846
+ row = this.getRowColumn(cell).row
847
+ index = row * this.columns + col + 1
848
+ while (index) {
849
+ --index
850
+ let component = this.components[index] as TableCell
851
+ // 슈퍼셀을 찾고 슈퍼셀의 위치에서 rowspan, colspan 거리만큼 이동하면서 cell이 있는지 검증해야함
852
+ if (component.rowspan > 1 || component.colspan > 1) {
853
+ let spColStart = this.getRowColumn(component).column
854
+ let spColEnd = this.getRowColumn(component).column + component.colspan
855
+ let spRowStart = this.getRowColumn(component).row
856
+ let spRowEnd = this.getRowColumn(component).row + component.rowspan
857
+ // 슈퍼셀 영역 안에 자식 셀이 있으면 superCells에 부모셀을 추가
858
+ if (col >= spColStart && col < spColEnd && (row >= spRowStart && row < spRowEnd)) {
859
+ if (-1 == superCellIndexes.indexOf(index)) {
860
+ superCellIndexes.push(index)
861
+ superCells.push(component)
862
+ }
863
+ }
864
+ }
865
+ }
866
+ })
867
+ superCellIndexes.forEach(index => {
868
+ // 추가하려는 셀은 일반 셀인데 그 위치에 다른 병합된 셀이 있는 문제로 임시로 막아 놓음. 수정해야함
869
+ if (superCellIndexes.length >= 2) return false
870
+ let superCellRow = Math.floor(index / this.columns)
871
+ let cell = this.components[index] as TableCell
872
+
873
+ let superCellObj = {
874
+ rowspan: cell.rowspan,
875
+ colspan: cell.colspan,
876
+ text: cell.get('text'),
877
+ merged: cell.merged
878
+ }
879
+ // 추가하려는 행이 병합된 셀중 마지막 행인 경우
880
+ if (superCellRow + superCellObj.rowspan - 1 === row) {
881
+ for (let i = 0; i < this.columns; i++) newbieCells.push(buildNewCell('table-cell', this.app))
882
+ newbieRowHeights.push(this.heights[row])
883
+
884
+ newbieCells.reverse().forEach(cell => {
885
+ this.insertComponentAt(cell, insertionRowPosition * this.columns)
886
+ })
887
+ } else if (superCellRow === row) {
888
+ for (let i = 0; i < this.columns; i++)
889
+ newbieCells.push(buildCopiedCell(this.components[row * this.columns + i].model, this.app))
890
+ newbieRowHeights.push(this.heights[row])
891
+
892
+ newbieCells.reverse().forEach(cell => {
893
+ this.insertComponentAt(cell, insertionRowPosition * this.columns)
894
+ })
895
+
896
+ cell.rowspan += 1
897
+
898
+ let newbieCell = this.components[index + this.columns] as TableCell
899
+ // 슈퍼셀이 복사됐으므로 그 해당 셀을 병합된 셀로 설정한다.
900
+ newbieCell.rowspan = 1
901
+ newbieCell.colspan = 1
902
+ newbieCell.merged = true
903
+ newbieCell.set('text', '')
904
+ } else {
905
+ for (let i = 0; i < this.columns; i++)
906
+ newbieCells.push(buildCopiedCell(this.components[row * this.columns + i].model, this.app))
907
+ newbieRowHeights.push(this.heights[row])
908
+
909
+ newbieCells.reverse().forEach(cell => {
910
+ this.insertComponentAt(cell, insertionRowPosition * this.columns)
911
+ })
912
+ cell.rowspan += 1
913
+ }
914
+
915
+ let heights = this.heights.slice()
916
+ heights.splice(insertionRowPosition, 0, ...newbieRowHeights)
917
+ this.set('heights', heights)
918
+
919
+ this.model.rows += 1
920
+
921
+ this.clearCache()
922
+ })
923
+ }
924
+ })
925
+ }
926
+
927
+ insertCellsLeft(cells: TableCell[]) {
928
+ // 먼저 cells 위치의 열을 구한다.
929
+ let columns: number[] = []
930
+ cells.forEach(cell => {
931
+ let column = this.getRowColumn(cell).column
932
+ if (-1 == columns.indexOf(column)) columns.push(column)
933
+ })
934
+ columns.sort((a, b) => {
935
+ return a - b
936
+ })
937
+ columns.reverse()
938
+ // 열 2개 이상은 추가 안함. 임시로 막아놓음
939
+ if (columns.length >= 2) return false
940
+ let insertionColumnPosition = columns[0]
941
+ let newbieColumnWidths: number[] = []
942
+ let newbieCells: TableCell[] = []
943
+ columns.forEach(column => {
944
+ // columns에서 세로 방향으로 이동하면서 병합된 셀을 찾는다.
945
+ let mergedCells = this.findMergedCellByY(column)
946
+ // mergedCells.length가 0이면 일반적으로 열을 왼쪽에 추가한다.
947
+ if (mergedCells.length === 0) {
948
+ for (let i = 0; i < this.rows; i++)
949
+ newbieCells.push(buildCopiedCell(this.components[column + this.columns * i].model, this.app))
950
+ newbieColumnWidths.push(this.widths[column])
951
+
952
+ let increasedColumns = this.columns
953
+ let index = this.rows
954
+ newbieCells.reverse().forEach(cell => {
955
+ if (index == 0) {
956
+ index = this.rows
957
+ increasedColumns++
958
+ }
959
+
960
+ index--
961
+ this.insertComponentAt(cell, insertionColumnPosition + index * increasedColumns)
962
+ })
963
+
964
+ let widths = this.widths.slice()
965
+ this.model.columns += columns.length // 고의적으로, change 이벤트가 발생하지 않도록 set(..)을 사용하지 않음.
966
+
967
+ widths.splice(insertionColumnPosition, 0, ...newbieColumnWidths)
968
+
969
+ this.set('widths', widths)
970
+ }
971
+ // mergedCells.length가 0이 아니면 병합된 셀을 고려하여 열을 추가해야 한다.
972
+ else {
973
+ // 부모 셀을 저장
974
+ let superCells = []
975
+ // 부모 셀의 인덱스 값을 저장
976
+ let superCellIndexes: number[] = []
977
+ mergedCells.forEach(cell => {
978
+ let col, row, index
979
+ col = this.getRowColumn(cell).column
980
+ row = this.getRowColumn(cell).row
981
+ index = row * this.columns + col + 1
982
+ while (index) {
983
+ --index
984
+ let component = this.components[index] as TableCell
985
+ // 슈퍼셀을 찾고 슈퍼셀의 위치에서 rowspan, colspan 거리만큼 이동하면서 cell이 있는지 검증해야함
986
+ if (component.rowspan > 1 || component.colspan > 1) {
987
+ let spColStart = this.getRowColumn(component).column
988
+ let spColEnd = this.getRowColumn(component).column + component.colspan
989
+ let spRowStart = this.getRowColumn(component).row
990
+ let spRowEnd = this.getRowColumn(component).row + component.rowspan
991
+ // 슈퍼셀 영역 안에 자식 셀이 있으면 superCells에 부모셀을 추가
992
+ if (col >= spColStart && col < spColEnd && (row >= spRowStart && row < spRowEnd)) {
993
+ if (-1 == superCellIndexes.indexOf(index)) {
994
+ superCellIndexes.push(index)
995
+ superCells.push(component)
996
+ }
997
+ }
998
+ }
999
+ }
1000
+ })
1001
+ superCellIndexes.forEach(index => {
1002
+ // 추가하려는 셀은 일반 셀인데 그 위치에 다른 병합된 셀이 있는 문제로 임시로 막아 놓음. 수정해야함
1003
+ if (superCellIndexes.length >= 2) return false
1004
+ let superCellColumn = index % this.columns
1005
+ let cell = this.components[index] as TableCell
1006
+
1007
+ let superCellObj = {
1008
+ rowspan: cell.rowspan,
1009
+ colspan: cell.colspan,
1010
+ text: cell.get('text'),
1011
+ merged: cell.merged
1012
+ }
1013
+ // 추가하려는 열이 슈퍼셀을 포함한 경우
1014
+ if (superCellColumn === column) {
1015
+ for (let i = 0; i < this.rows; i++) newbieCells.push(buildNewCell('table-cell', this.app))
1016
+ newbieColumnWidths.push(this.widths[column])
1017
+
1018
+ let increasedColumns = this.columns
1019
+ let rowIndex = this.rows
1020
+ newbieCells.reverse().forEach(cell => {
1021
+ if (rowIndex == 0) {
1022
+ rowIndex = this.rows
1023
+ increasedColumns++
1024
+ }
1025
+
1026
+ rowIndex--
1027
+ this.insertComponentAt(cell, insertionColumnPosition + rowIndex * increasedColumns)
1028
+ })
1029
+ } else {
1030
+ cell.colspan += 1
1031
+ for (let i = 0; i < this.rows; i++)
1032
+ newbieCells.push(buildCopiedCell(this.components[column + this.columns * i].model, this.app))
1033
+ newbieColumnWidths.push(this.widths[column])
1034
+
1035
+ let increasedColumns = this.columns
1036
+ let rowIndex = this.rows
1037
+ newbieCells.reverse().forEach(cell => {
1038
+ if (rowIndex == 0) {
1039
+ rowIndex = this.rows
1040
+ increasedColumns++
1041
+ }
1042
+
1043
+ rowIndex--
1044
+ this.insertComponentAt(cell, insertionColumnPosition + rowIndex * increasedColumns)
1045
+ })
1046
+ }
1047
+ let widths = this.widths.slice()
1048
+ this.model.columns += columns.length // 고의적으로, change 이벤트가 발생하지 않도록 set(..)을 사용하지 않음.
1049
+
1050
+ widths.splice(insertionColumnPosition, 0, ...newbieColumnWidths)
1051
+
1052
+ this.set('widths', widths)
1053
+ })
1054
+ }
1055
+ })
1056
+ }
1057
+
1058
+ insertCellsRight(cells: TableCell[]) {
1059
+ // 먼저 cells 위치의 열을 구한다.
1060
+ let columns: number[] = []
1061
+ cells.forEach(cell => {
1062
+ let column = this.getRowColumn(cell).column
1063
+ if (-1 == columns.indexOf(column)) columns.push(column)
1064
+ })
1065
+ columns.sort((a, b) => {
1066
+ return a - b
1067
+ })
1068
+ columns.reverse()
1069
+ // 열 2개 이상은 추가 안함. 임시로 막아놓음
1070
+ if (columns.length >= 2) return false
1071
+ let insertionColumnPosition = columns[columns.length - 1] + 1
1072
+ let newbieColumnWidths: number[] = []
1073
+ let newbieCells: TableCell[] = []
1074
+ columns.forEach(column => {
1075
+ // columns에서 세로 방향으로 이동하면서 병합된 셀을 찾는다.
1076
+ let mergedCells = this.findMergedCellByY(column)
1077
+ // mergedCells.length가 0이면 일반적으로 열을 오른쪽에 추가한다.
1078
+ if (mergedCells.length === 0) {
1079
+ for (let i = 0; i < this.rows; i++)
1080
+ newbieCells.push(buildCopiedCell(this.components[column + this.columns * i].model, this.app))
1081
+ newbieColumnWidths.push(this.widths[column])
1082
+
1083
+ let increasedColumns = this.columns
1084
+ let index = this.rows
1085
+ newbieCells.reverse().forEach(cell => {
1086
+ if (index == 0) {
1087
+ index = this.rows
1088
+ increasedColumns++
1089
+ }
1090
+
1091
+ index--
1092
+ this.insertComponentAt(cell, insertionColumnPosition + index * increasedColumns)
1093
+ })
1094
+
1095
+ let widths = this.widths.slice()
1096
+ this.model.columns += columns.length // 고의적으로, change 이벤트가 발생하지 않도록 set(..)을 사용하지 않음.
1097
+
1098
+ widths.splice(insertionColumnPosition, 0, ...newbieColumnWidths)
1099
+
1100
+ this.set('widths', widths)
1101
+ }
1102
+ // mergedCells.length가 0이 아니면 병합된 셀을 고려하여 열을 추가해야 한다.
1103
+ else {
1104
+ // 부모 셀을 저장
1105
+ let superCells = []
1106
+ // 부모 셀의 인덱스 값을 저장
1107
+ let superCellIndexes: number[] = []
1108
+ mergedCells.forEach(cell => {
1109
+ let col, row, index
1110
+ col = this.getRowColumn(cell).column
1111
+ row = this.getRowColumn(cell).row
1112
+ index = row * this.columns + col + 1
1113
+ while (index) {
1114
+ --index
1115
+ let component = this.components[index] as TableCell
1116
+ // 슈퍼셀을 찾고 슈퍼셀의 위치에서 rowspan, colspan 거리만큼 이동하면서 cell이 있는지 검증해야함
1117
+ if (component.rowspan > 1 || component.colspan > 1) {
1118
+ let spColStart = this.getRowColumn(component).column
1119
+ let spColEnd = this.getRowColumn(component).column + component.colspan
1120
+ let spRowStart = this.getRowColumn(component).row
1121
+ let spRowEnd = this.getRowColumn(component).row + component.rowspan
1122
+ // 슈퍼셀 영역 안에 자식 셀이 있으면 superCells에 부모셀을 추가
1123
+ if (col >= spColStart && col < spColEnd && (row >= spRowStart && row < spRowEnd)) {
1124
+ if (-1 == superCellIndexes.indexOf(index)) {
1125
+ superCellIndexes.push(index)
1126
+ superCells.push(component)
1127
+ }
1128
+ }
1129
+ }
1130
+ }
1131
+ })
1132
+ superCellIndexes.forEach(index => {
1133
+ // 추가하려는 셀은 일반 셀인데 그 위치에 다른 병합된 셀이 있는 문제로 임시로 막아 놓음. 수정해야함
1134
+ if (superCellIndexes.length >= 2) return false
1135
+ let superCellRow = Math.floor(index / this.columns)
1136
+ let superCellColumn = index % this.columns
1137
+ let cell = this.components[index] as TableCell
1138
+
1139
+ let superCellObj = {
1140
+ rowspan: cell.rowspan,
1141
+ colspan: cell.colspan,
1142
+ text: cell.get('text'),
1143
+ merged: cell.merged
1144
+ }
1145
+ // 추가하려는 열이 병합된 셀중 마지막 열인 경우
1146
+ if (superCellColumn + superCellObj.colspan - 1 === column) {
1147
+ for (let i = 0; i < this.rows; i++) newbieCells.push(buildNewCell('table-cell', this.app))
1148
+ newbieColumnWidths.push(this.widths[column])
1149
+
1150
+ let increasedColumns = this.columns
1151
+ let rowIndex = this.rows
1152
+ newbieCells.reverse().forEach(cell => {
1153
+ if (rowIndex == 0) {
1154
+ rowIndex = this.rows
1155
+ increasedColumns++
1156
+ }
1157
+
1158
+ rowIndex--
1159
+ this.insertComponentAt(cell, insertionColumnPosition + rowIndex * increasedColumns)
1160
+ })
1161
+ } else if (superCellColumn === column) {
1162
+ cell.colspan += 1
1163
+ for (let i = 0; i < this.rows; i++)
1164
+ newbieCells.push(buildCopiedCell(this.components[column + this.columns * i].model, this.app))
1165
+ newbieColumnWidths.push(this.widths[column])
1166
+
1167
+ let increasedColumns = this.columns
1168
+ let rowIndex = this.rows
1169
+ newbieCells.reverse().forEach(cell => {
1170
+ if (rowIndex == 0) {
1171
+ rowIndex = this.rows
1172
+ increasedColumns++
1173
+ }
1174
+
1175
+ rowIndex--
1176
+ this.insertComponentAt(cell, insertionColumnPosition + rowIndex * increasedColumns)
1177
+ })
1178
+ // 슈퍼셀이 복사됐으므로 그 해당 셀을 병합된 셀로 설정한다.
1179
+ let newbieCell = this.components[index + superCellRow + 1] as TableCell
1180
+ newbieCell.rowspan = 1
1181
+ newbieCell.colspan = 1
1182
+ newbieCell.merged = true
1183
+ newbieCell.set('text', '')
1184
+ } else {
1185
+ cell.colspan += 1
1186
+ for (let i = 0; i < this.rows; i++)
1187
+ newbieCells.push(buildCopiedCell(this.components[column + this.columns * i].model, this.app))
1188
+ newbieColumnWidths.push(this.widths[column])
1189
+
1190
+ let increasedColumns = this.columns
1191
+ let rowIndex = this.rows
1192
+ newbieCells.reverse().forEach(cell => {
1193
+ if (rowIndex == 0) {
1194
+ rowIndex = this.rows
1195
+ increasedColumns++
1196
+ }
1197
+
1198
+ rowIndex--
1199
+ this.insertComponentAt(cell, insertionColumnPosition + rowIndex * increasedColumns)
1200
+ })
1201
+ }
1202
+ let widths = this.widths.slice()
1203
+ this.model.columns += columns.length // 고의적으로, change 이벤트가 발생하지 않도록 set(..)을 사용하지 않음.
1204
+
1205
+ widths.splice(insertionColumnPosition, 0, ...newbieColumnWidths)
1206
+
1207
+ this.set('widths', widths)
1208
+ })
1209
+ }
1210
+ })
1211
+ }
1212
+
1213
+ distributeHorizontal(cells: TableCell[]) {
1214
+ var columns: number[] = []
1215
+
1216
+ cells.forEach(cell => {
1217
+ let rowcolumn = this.getRowColumn(cell)
1218
+
1219
+ if (-1 == columns.indexOf(rowcolumn.column)) columns.push(rowcolumn.column)
1220
+ })
1221
+
1222
+ var sum = columns.reduce((sum, column) => {
1223
+ return sum + this.widths[column]
1224
+ }, 0)
1225
+
1226
+ var newval = Math.round((sum / columns.length) * 100) / 100
1227
+ var widths = this.widths.slice()
1228
+ columns.forEach(column => {
1229
+ widths[column] = newval
1230
+ })
1231
+
1232
+ this.set('widths', widths)
1233
+ }
1234
+
1235
+ distributeVertical(cells: TableCell[]) {
1236
+ var rows: number[] = []
1237
+
1238
+ cells.forEach((cell: TableCell) => {
1239
+ let rowcolumn = this.getRowColumn(cell)
1240
+
1241
+ if (-1 == rows.indexOf(rowcolumn.row)) rows.push(rowcolumn.row)
1242
+ })
1243
+
1244
+ var sum: number = rows.reduce((sum, row) => {
1245
+ return sum + this.heights[row]
1246
+ }, 0)
1247
+
1248
+ var newval = Math.round((sum / rows.length) * 100) / 100
1249
+ var heights = this.heights.slice()
1250
+ rows.forEach(row => {
1251
+ heights[row] = newval
1252
+ })
1253
+
1254
+ this.set('heights', heights)
1255
+ }
1256
+
1257
+ toObjectArrayValue(array: any[]) {
1258
+ if (!array || array.length === 0) return null
1259
+
1260
+ if (!array[0].hasOwnProperty('__field1')) {
1261
+ return array
1262
+ }
1263
+
1264
+ let indexKeyMap: {[key: string]: any} = {}
1265
+ let value = []
1266
+
1267
+ for (let key in array[0]) {
1268
+ indexKeyMap[key] = array[0][key]
1269
+ }
1270
+
1271
+ for (var i = 1; i < array.length; i++) {
1272
+ let object: {[key: string]: any} = {}
1273
+ let thisObject = array[i]
1274
+ for (let key in indexKeyMap) {
1275
+ let k = indexKeyMap[key]
1276
+ let v = thisObject[key]
1277
+ object[k] = v
1278
+ }
1279
+
1280
+ value.push(object)
1281
+ }
1282
+
1283
+ return value
1284
+ }
1285
+
1286
+ get columns() {
1287
+ return Number(this.get('columns'))
1288
+ }
1289
+
1290
+ get lefts() {
1291
+ return this.components.filter((c, i) => {
1292
+ return !(i % this.columns)
1293
+ })
1294
+ }
1295
+
1296
+ get centers() {
1297
+ return this.components.filter((c, i) => {
1298
+ return i % this.columns && (i + 1) % this.columns
1299
+ })
1300
+ }
1301
+
1302
+ get rights() {
1303
+ return this.components.filter((c, i) => {
1304
+ return !((i + 1) % this.columns)
1305
+ })
1306
+ }
1307
+
1308
+ get tops() {
1309
+ return this.components.slice(0, this.columns)
1310
+ }
1311
+
1312
+ get middles() {
1313
+ return this.components.slice(this.columns, this.columns * (this.rows - 1))
1314
+ }
1315
+
1316
+ get bottoms() {
1317
+ return this.components.slice(this.columns * (this.rows - 1))
1318
+ }
1319
+
1320
+ get widths_sum(): number {
1321
+ var widths = this.widths
1322
+ return widths ? widths.filter((width: number, i: number) => i < this.columns).reduce((sum: number, width: number) => sum + width, 0) : this.columns
1323
+ }
1324
+
1325
+ get heights_sum(): number {
1326
+ var heights = this.heights
1327
+ return heights ? heights.filter((height: number, i: number) => i < this.rows).reduce((sum: number, height: number) => sum + height, 0) : this.rows
1328
+ }
1329
+
1330
+ get nature(): ComponentNature {
1331
+ return NATURE
1332
+ }
1333
+
1334
+ get controls(): Control[] {
1335
+ var widths = this.widths
1336
+ var heights = this.heights
1337
+ var inside = this.textBounds
1338
+
1339
+ var width_unit = inside!.width / this.widths_sum
1340
+ var height_unit = inside!.height / this.heights_sum
1341
+
1342
+ var x = inside!.left
1343
+ var y = inside!.top
1344
+
1345
+ var controls: Control[] = []
1346
+
1347
+ widths.slice(0, this.columns - 1).forEach((width: number) => {
1348
+ x += width * width_unit
1349
+ controls.push({
1350
+ x: x,
1351
+ y: inside!.top,
1352
+ handler: columnControlHandler
1353
+ })
1354
+ })
1355
+
1356
+ heights.slice(0, this.rows - 1).forEach((height: number) => {
1357
+ y += height * height_unit
1358
+ controls.push({
1359
+ x: inside!.left,
1360
+ y: y,
1361
+ handler: rowControlHandler
1362
+ })
1363
+ })
1364
+
1365
+ return controls
1366
+ }
1367
+
1368
+ onchange(after: State, before: State) {
1369
+ if ('rows' in after || 'columns' in after) {
1370
+ let { rows, columns } = this
1371
+
1372
+ this.buildCells(
1373
+ rows,
1374
+ columns,
1375
+ 'rows' in before ? before.rows : rows,
1376
+ 'columns' in before ? before.columns : columns
1377
+ )
1378
+ }
1379
+
1380
+ if ('data' in after) {
1381
+ this.setCellsData()
1382
+ }
1383
+ }
1384
+
1385
+ get eventMap() {
1386
+ return {
1387
+ '(self)': {
1388
+ '(descendant)': {
1389
+ change: this.oncellchanged
1390
+ }
1391
+ }
1392
+ }
1393
+ }
1394
+
1395
+ oncellchanged(after: State, before: State) {
1396
+ if ('dataKey' in after || 'dataIndex' in after) {
1397
+ this.setCellsData()
1398
+ }
1399
+ }
1400
+ }
1401
+
1402
+ ;['rows', 'columns', 'widths', 'heights', 'widths_sum', 'heights_sum', 'controls'].forEach(getter =>
1403
+ Component.memoize(Table.prototype, getter, false)
1404
+ )
1405
+
1406
+ Component.register('table', Table)