@operato/scene-table 7.3.9 → 7.3.19

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.
@@ -1,674 +0,0 @@
1
- /*
2
- * Copyright © HatioLab Inc. All rights reserved.
3
- */
4
-
5
- import './data-cell'
6
- import './data-list-layout'
7
-
8
- import { Component, ComponentNature, Container, Control, Layout, Model, State, Style } from '@hatiolab/things-scene'
9
-
10
- import {
11
- above,
12
- after,
13
- array,
14
- before,
15
- below,
16
- buildCopiedCell,
17
- buildNewCell,
18
- CLEAR_STYLE,
19
- columnControlHandler,
20
- isBottomMost,
21
- isLeftMost,
22
- isRightMost,
23
- isTopMost,
24
- rowControlHandler,
25
- setCellBorder,
26
- WHERE
27
- } from '../helper-functions'
28
-
29
- const NATURE: ComponentNature = {
30
- mutable: false,
31
- resizable: true,
32
- rotatable: true,
33
- properties: [
34
- {
35
- type: 'number',
36
- label: 'columns',
37
- name: 'columns',
38
- property: 'columns'
39
- }
40
- ],
41
- 'value-property': 'data',
42
- help: 'scene/component/data-list'
43
- }
44
-
45
- export default class DataList extends Container {
46
- postrender(context: CanvasRenderingContext2D) {
47
- super.postrender(context)
48
-
49
- // child component들을 다 그린 후에 scrollbar를 표현한다.
50
- if (this.app.isViewMode) {
51
- this.renderScrollbar(context)
52
- }
53
- }
54
-
55
- renderScrollbar(context: CanvasRenderingContext2D) {
56
- var { left, top, width, height } = this.bounds
57
- var { offset = { x: 0, y: 0 }, data } = this.state
58
-
59
- var fullHeight = ((data && data.length) || 0) * (this.heights[0] / this.heights_sum) * height
60
-
61
- if (fullHeight <= height) {
62
- return
63
- }
64
-
65
- var start = (-offset.y / fullHeight) * height
66
- var end = ((-offset.y + height) / fullHeight) * height
67
-
68
- context.strokeStyle = 'gray'
69
- context.lineWidth = 10
70
- context.globalAlpha = 0.3
71
-
72
- context.beginPath()
73
- context.moveTo(left + width - 10, top + start)
74
- context.lineTo(left + width - 10, top + end)
75
-
76
- context.stroke()
77
- }
78
-
79
- created() {
80
- this.set('rows', 2)
81
-
82
- var rows = 1
83
-
84
- var tobeSize = rows * this.columns
85
- var gap = this.size() - tobeSize
86
-
87
- if (this.data) {
88
- this.setCellsData()
89
- }
90
-
91
- if (gap == 0) {
92
- return
93
- } else if (gap > 0) {
94
- let removals = this.components.slice(gap)
95
- this.remove(removals)
96
- } else {
97
- let newbies = []
98
-
99
- for (let i = 0; i < -gap; i++) newbies.push(buildNewCell('data-cell', this.app))
100
-
101
- this.add(newbies)
102
- }
103
-
104
- var widths = this.getState('widths')
105
- var heights = this.getState('heights')
106
-
107
- if (!widths || widths.length < this.columns) this.set('widths', this.widths)
108
- if (!heights || heights.length < rows) this.set('heights', this.heights)
109
- }
110
-
111
- _onwheel(e: WheelEvent) {
112
- e.stopPropagation()
113
-
114
- var { height } = this.bounds
115
- var { offset = { x: 0, y: 0 } } = this.state
116
-
117
- var recordHeight = (this.heights[0] / this.heights.reduce((sum: number, height: number) => sum + height)) * height
118
-
119
- var minX = 0
120
- var minY = this.data && this.data.length ? Math.min(-recordHeight * this.data.length + height, 0) : 0
121
-
122
- /* shiftKey + wheel 은 deltaX 값을 변화시킨다. */
123
- if (e.deltaY == 0 && e.deltaX == 0) return
124
-
125
- var x = e.deltaX + offset.x
126
- var y = -e.deltaY + offset.y
127
-
128
- /* 휠을 밀면.. 화면은 위쪽으로 : scroll down */
129
- /* 휠을 당기면.. 화면은 아래쪽으로 : scroll up */
130
-
131
- this.setState({
132
- offset: {
133
- x: Math.max(Math.min(0, x), minX),
134
- y: Math.max(Math.min(0, y), minY)
135
- }
136
- })
137
- }
138
-
139
- private __START_OFFSET?: { x: number; y: number }
140
- private __START_Y?: number
141
-
142
- _ontouchstart(e: DragEvent) {
143
- this.__START_OFFSET = this.state.offset || {
144
- x: 0,
145
- y: 0
146
- }
147
- this.__START_Y = e.offsetY
148
- }
149
-
150
- _ontouchmove(e: DragEvent) {
151
- if (!this.__START_OFFSET) {
152
- return
153
- }
154
-
155
- var { height } = this.bounds
156
-
157
- var recordHeight = (this.heights[0] / this.heights.reduce((sum: number, height: number) => sum + height)) * height
158
- var minY = this.data && this.data.length ? Math.min(-recordHeight * this.data.length + height, 0) : 0
159
-
160
- var x = 0
161
- var y = this.__START_OFFSET.y + (e.offsetY - this.__START_Y!) / this.rootModel.state.scale.y
162
- y = Math.max(Math.min(0, y), minY)
163
-
164
- var offset = this.state.offset || { x: 0, y: 0 }
165
- if (offset.x !== x || offset.y !== y) {
166
- this.setState('offset', {
167
- x,
168
- y
169
- })
170
-
171
- e.stopPropagation()
172
- }
173
- }
174
-
175
- _ontouchend(e: DragEvent) {
176
- delete this.__START_OFFSET
177
- delete this.__START_Y
178
- }
179
-
180
- get layout() {
181
- return Layout.get('data-list')
182
- }
183
-
184
- containable(component: Component) {
185
- return component.getState('type') == 'data-cell'
186
- }
187
-
188
- get focusible() {
189
- /* 이 컨테이너에는 컴포넌트를 임의로 추가 및 삭제할 수 없도록 함 */
190
- return false
191
- }
192
-
193
- get widths_sum(): number {
194
- var widths = this.widths
195
- return widths
196
- ? widths
197
- .filter((width: number, i: number) => i < this.columns)
198
- .reduce((sum: number, width: number) => sum + width, 0)
199
- : this.columns
200
- }
201
-
202
- get heights_sum(): number {
203
- var heights = this.heights
204
- return heights ? heights.filter((height, i) => i < this.rows).reduce((sum, height) => sum + height, 0) : this.rows
205
- }
206
-
207
- get widths(): number[] {
208
- var widths = this.getState('widths')
209
-
210
- if (!widths) return array(1, this.columns)
211
-
212
- if (widths.length < this.columns) return widths.concat(array(1, this.columns - widths.length))
213
- else if (widths.length > this.columns) return widths.slice(0, this.columns)
214
-
215
- return widths
216
- }
217
-
218
- get heights(): number[] {
219
- var heights = this.getState('heights')
220
- var rows = 2
221
-
222
- if (!heights) {
223
- return array(1, rows)
224
- }
225
-
226
- if (heights.length < rows) return heights.concat(array(1, rows - heights.length))
227
- else if (heights.length > rows) return heights.slice(0, rows)
228
-
229
- return heights
230
- }
231
-
232
- get columns() {
233
- return Number(this.getState('columns'))
234
- }
235
-
236
- get rows(): number {
237
- /* 1 for first record fields, 1 for rest record fields */
238
- return 2
239
- }
240
-
241
- get nature(): ComponentNature {
242
- return NATURE
243
- }
244
-
245
- get controls(): Control[] {
246
- var widths = this.widths
247
- var heights = this.heights
248
- var inside = this.textBounds
249
-
250
- var width_unit = inside.width / this.widths_sum
251
- var height_unit = inside.height / this.heights_sum
252
-
253
- var x = inside.left
254
- var y = inside.top
255
-
256
- var controls: Control[] = []
257
-
258
- widths.slice(0, this.columns - 1).forEach(width => {
259
- x += width * width_unit
260
- controls.push({
261
- x: x,
262
- y: inside.top,
263
- handler: columnControlHandler
264
- })
265
- })
266
-
267
- heights.slice(0, this.rows - 1).forEach(height => {
268
- y += height * height_unit
269
- controls.push({
270
- x: inside.left,
271
- y: y,
272
- handler: rowControlHandler
273
- })
274
- })
275
-
276
- return controls
277
- }
278
-
279
- onchange(after: State, before: State) {
280
- if ('data' in after) {
281
- this.setCellsData()
282
- }
283
-
284
- if ('columns' in after) {
285
- let { columns } = this
286
-
287
- this.buildCells(columns, Number(before.columns))
288
- }
289
- }
290
-
291
- get eventMap() {
292
- return {
293
- '(self)': {
294
- '(all)': {
295
- wheel: this._onwheel,
296
- touchstart: this._ontouchstart,
297
- touchmove: this._ontouchmove,
298
- touchend: this._ontouchend
299
- }
300
- }
301
- }
302
- }
303
-
304
- setCellsData() {
305
- if (!this.app.isViewMode) {
306
- return
307
- }
308
-
309
- var data = this.data || []
310
- if (!(data instanceof Array)) {
311
- data = [data]
312
- }
313
-
314
- /* 기존의 레코드를 모두 삭제 (템플릿 레코드만 남긴다.) */
315
- this.remove(this.components.slice(this.columns), true /* silent - do not let root-container refreshMappings */)
316
-
317
- /* template 레코드의 데이터를 클리어시킨다 */
318
- var templates = this.getCellsByRow(0)
319
- templates.forEach(field => {
320
- field.data = ''
321
- })
322
-
323
- /* 데이터의 크기만큼 새로운 레코드를 만든다 */
324
- if (data.length > 1) {
325
- let newbies: Component[] = []
326
-
327
- for (let i = 1; i < data.length; i++) {
328
- newbies = newbies.concat(
329
- templates.map(field => {
330
- const { refid, id, data, ...org } = field.model
331
- return Model.compile(org, this.app)
332
- })
333
- )
334
- }
335
-
336
- this.add(newbies, true /* silent - do not let root-container refreshMappings */)
337
- }
338
-
339
- data.forEach((record: any[], idx: number) => {
340
- let data = {
341
- _idx: idx,
342
- ...record
343
- }
344
- let row = this.getCellsByRow(idx)
345
- row.forEach(field => {
346
- // @ts-ignore TODO - remove this comment later
347
- field.value = data
348
- })
349
- })
350
-
351
- /* 스크롤 위치를 조정 */
352
- var { height } = this.bounds
353
- var fullHeight = data.length * (this.heights[0] / this.heights_sum) * height
354
-
355
- if (fullHeight <= height) {
356
- // 데이터가 화면보다 작을 경우, 스크롤 위치를 최상단으로 이동
357
- this.setState({
358
- offset: {
359
- x: 0,
360
- y: 0
361
- }
362
- })
363
- } else {
364
- // 데이터가 화면보다 클 경우, 현재 스크롤 위치를 보정
365
- var minY = Math.min(-fullHeight + height, 0)
366
- var { offset = { x: 0, y: 0 } } = this.state
367
-
368
- this.setState({
369
- offset: {
370
- x: 0,
371
- y: Math.max(minY, offset.y)
372
- }
373
- })
374
- }
375
- }
376
-
377
- setCellsStyle(cells: Component[], style: Style, where: WHERE, clearBefore = true) {
378
- var components = this.components
379
- var total = components.length
380
- var columns = this.getState('columns')
381
-
382
- var indices = cells.map(cell => components.indexOf(cell))
383
- indices.forEach(i => {
384
- var cell = components[i]
385
- clearBefore && setCellBorder(cell, CLEAR_STYLE, 'all')
386
-
387
- switch (where) {
388
- case 'all':
389
- setCellBorder(cell, style, where)
390
-
391
- if (isLeftMost(total, columns, indices, i)) setCellBorder(components[before(columns, i)], style, 'right')
392
- if (isRightMost(total, columns, indices, i)) setCellBorder(components[after(columns, i)], style, 'left')
393
- if (isTopMost(total, columns, indices, i)) setCellBorder(components[above(columns, i)], style, 'bottom')
394
- if (isBottomMost(total, columns, indices, i)) setCellBorder(components[below(columns, i)], style, 'top')
395
- break
396
- case 'in':
397
- if (!isLeftMost(total, columns, indices, i)) {
398
- setCellBorder(cell, style, 'left')
399
- }
400
- if (!isRightMost(total, columns, indices, i)) {
401
- setCellBorder(cell, style, 'right')
402
- }
403
- if (!isTopMost(total, columns, indices, i)) {
404
- setCellBorder(cell, style, 'top')
405
- }
406
- if (!isBottomMost(total, columns, indices, i)) {
407
- setCellBorder(cell, style, 'bottom')
408
- }
409
- break
410
- case 'out':
411
- if (isLeftMost(total, columns, indices, i)) {
412
- setCellBorder(cell, style, 'left')
413
- setCellBorder(components[before(columns, i)], style, 'right')
414
- }
415
- if (isRightMost(total, columns, indices, i)) {
416
- setCellBorder(cell, style, 'right')
417
- setCellBorder(components[after(columns, i)], style, 'left')
418
- }
419
- if (isTopMost(total, columns, indices, i)) {
420
- setCellBorder(cell, style, 'top')
421
- setCellBorder(components[above(columns, i)], style, 'bottom')
422
- }
423
- if (isBottomMost(total, columns, indices, i)) {
424
- setCellBorder(cell, style, 'bottom')
425
- setCellBorder(components[below(columns, i)], style, 'top')
426
- }
427
- break
428
- case 'left':
429
- if (isLeftMost(total, columns, indices, i)) {
430
- setCellBorder(cell, style, 'left')
431
- setCellBorder(components[before(columns, i)], style, 'right')
432
- }
433
- break
434
- case 'right':
435
- if (isRightMost(total, columns, indices, i)) {
436
- setCellBorder(cell, style, 'right')
437
- setCellBorder(components[after(columns, i)], style, 'left')
438
- }
439
- break
440
- case 'center':
441
- if (!isLeftMost(total, columns, indices, i)) {
442
- setCellBorder(cell, style, 'left')
443
- }
444
- if (!isRightMost(total, columns, indices, i)) {
445
- setCellBorder(cell, style, 'right')
446
- }
447
- break
448
- case 'middle':
449
- if (!isTopMost(total, columns, indices, i)) {
450
- setCellBorder(cell, style, 'top')
451
- }
452
- if (!isBottomMost(total, columns, indices, i)) {
453
- setCellBorder(cell, style, 'bottom')
454
- }
455
- break
456
- case 'top':
457
- if (isTopMost(total, columns, indices, i)) {
458
- setCellBorder(cell, style, 'top')
459
- setCellBorder(components[above(columns, i)], style, 'bottom')
460
- }
461
- break
462
- case 'bottom':
463
- if (isBottomMost(total, columns, indices, i)) {
464
- setCellBorder(cell, style, 'bottom')
465
- setCellBorder(components[below(columns, i)], style, 'top')
466
- }
467
- break
468
- case 'clear':
469
- setCellBorder(cell, CLEAR_STYLE, 'all')
470
-
471
- if (isLeftMost(total, columns, indices, i))
472
- setCellBorder(components[before(columns, i)], CLEAR_STYLE, 'right')
473
- if (isRightMost(total, columns, indices, i)) setCellBorder(components[after(columns, i)], CLEAR_STYLE, 'left')
474
- if (isTopMost(total, columns, indices, i)) setCellBorder(components[above(columns, i)], CLEAR_STYLE, 'bottom')
475
- if (isBottomMost(total, columns, indices, i)) setCellBorder(components[below(columns, i)], CLEAR_STYLE, 'top')
476
- }
477
- })
478
- }
479
-
480
- buildCells(newcolumns: number, oldcolumns: number) {
481
- var minrows = 1
482
-
483
- if (newcolumns > oldcolumns) {
484
- for (let r = 0; r < minrows; r++) {
485
- for (let c = oldcolumns; c < newcolumns; c++) {
486
- this.insertComponentAt(buildNewCell('data-cell', this.app), r * newcolumns + c)
487
- }
488
- }
489
- } else if (newcolumns < oldcolumns) {
490
- let removals = []
491
-
492
- for (let r = 0; r < minrows; r++) {
493
- for (let c = newcolumns; c < oldcolumns; c++) {
494
- removals.push(this.components[r * oldcolumns + c])
495
- }
496
- }
497
-
498
- this.remove(removals)
499
- }
500
-
501
- this.set({
502
- widths: this.widths,
503
- heights: this.heights
504
- })
505
-
506
- this.setCellsData()
507
- }
508
-
509
- getRowColumn(cell: Component) {
510
- var idx = this.components.indexOf(cell)
511
-
512
- return {
513
- column: idx % this.columns,
514
- row: Math.floor(idx / this.columns)
515
- }
516
- }
517
-
518
- getCellsByRow(row: number): Component[] {
519
- return this.components.slice(row * this.columns, (row + 1) * this.columns)
520
- }
521
-
522
- getCellsByColumn(column: number): Component[] {
523
- var cells = []
524
- for (let i = 0; i < this.rows; i++) cells.push(this.components[this.columns * i + column])
525
-
526
- return cells
527
- }
528
-
529
- mergeCells(cells: Component[]) {}
530
-
531
- splitCells(cells: Component[]) {}
532
-
533
- deleteRows(cells: Component[]) {}
534
-
535
- deleteColumns(cells: Component[]) {
536
- // 만약 선택한 셀이 병합된 셀이라면 삭제하지 않는다.
537
- // if (cells[0].merged == true) return false
538
-
539
- // 먼저 cells 위치의 열을 구한다.
540
- let columns: number[] = []
541
- cells.forEach(cell => {
542
- let column = this.getRowColumn(cell).column
543
- if (-1 == columns.indexOf(column)) columns.push(column)
544
- })
545
- columns.sort((a, b) => {
546
- return a - b
547
- })
548
- columns.reverse()
549
-
550
- columns.forEach(column => {
551
- var widths = this.widths.slice()
552
- this.remove(this.getCellsByColumn(column))
553
-
554
- widths.splice(column, 1)
555
- this.model.columns -= 1 // 고의적으로, change 이벤트가 발생하지 않도록 set(..)을 사용하지 않음.
556
- this.set('widths', widths)
557
- })
558
- }
559
-
560
- insertCellsAbove(cells: Component[]) {}
561
-
562
- insertCellsBelow(cells: Component[]) {}
563
-
564
- insertCellsLeft(cells: Component[]) {
565
- // 먼저 cells 위치의 열을 구한다.
566
- let columns: number[] = []
567
- cells.forEach(cell => {
568
- let column = this.getRowColumn(cell).column
569
- if (-1 == columns.indexOf(column)) columns.push(column)
570
- })
571
- columns.sort((a, b) => {
572
- return a - b
573
- })
574
- columns.reverse()
575
-
576
- // 열 2개 이상은 추가 안함. 임시로 막아놓음
577
- if (columns.length >= 2) return false
578
- let insertionColumnPosition = columns[0]
579
- let newbieColumnWidths: number[] = []
580
- let newbieCells: Component[] = []
581
- columns.forEach(column => {
582
- for (let i = 0; i < this.rows; i++)
583
- newbieCells.push(buildCopiedCell(this.components[column + this.columns * i].model, this.app))
584
- newbieColumnWidths.push(this.widths[column])
585
-
586
- let increasedColumns = this.columns
587
- let index = this.rows
588
- newbieCells.reverse().forEach(cell => {
589
- if (index == 0) {
590
- index = this.rows
591
- increasedColumns++
592
- }
593
-
594
- index--
595
- this.insertComponentAt(cell, insertionColumnPosition + index * increasedColumns)
596
- })
597
-
598
- let widths = this.widths.slice()
599
- this.model.columns += columns.length // 고의적으로, change 이벤트가 발생하지 않도록 set(..)을 사용하지 않음.
600
-
601
- widths.splice(insertionColumnPosition, 0, ...newbieColumnWidths)
602
-
603
- this.set('widths', widths)
604
- })
605
- }
606
-
607
- insertCellsRight(cells: Component[]) {
608
- // 먼저 cells 위치의 열을 구한다.
609
- let columns: number[] = []
610
- cells.forEach(cell => {
611
- let column = this.getRowColumn(cell).column
612
- if (-1 == columns.indexOf(column)) columns.push(column)
613
- })
614
- columns.sort((a, b) => {
615
- return a - b
616
- })
617
- columns.reverse()
618
- // 열 2개 이상은 추가 안함. 임시로 막아놓음
619
- if (columns.length >= 2) return false
620
- let insertionColumnPosition = columns[columns.length - 1] + 1
621
- let newbieColumnWidths: number[] = []
622
- let newbieCells: Component[] = []
623
- columns.forEach(column => {
624
- for (let i = 0; i < this.rows; i++)
625
- newbieCells.push(buildCopiedCell(this.components[column + this.columns * i].model, this.app))
626
- newbieColumnWidths.push(this.widths[column])
627
-
628
- let increasedColumns = this.columns
629
- let index = this.rows
630
- newbieCells.reverse().forEach(cell => {
631
- if (index == 0) {
632
- index = this.rows
633
- increasedColumns++
634
- }
635
-
636
- index--
637
- this.insertComponentAt(cell, insertionColumnPosition + index * increasedColumns)
638
- })
639
-
640
- let widths = this.widths.slice()
641
- this.model.columns += columns.length // 고의적으로, change 이벤트가 발생하지 않도록 set(..)을 사용하지 않음.
642
-
643
- widths.splice(insertionColumnPosition, 0, ...newbieColumnWidths)
644
-
645
- this.set('widths', widths)
646
- })
647
- }
648
-
649
- distributeHorizontal(cells: Component[]) {
650
- var columns: number[] = []
651
-
652
- cells.forEach(cell => {
653
- let rowcolumn = this.getRowColumn(cell)
654
-
655
- if (-1 == columns.indexOf(rowcolumn.column)) columns.push(rowcolumn.column)
656
- })
657
-
658
- var sum = columns.reduce((sum, column) => {
659
- return sum + this.widths[column]
660
- }, 0)
661
-
662
- var newval = Math.round((sum / columns.length) * 100) / 100
663
- var widths = this.widths.slice()
664
- columns.forEach(column => {
665
- widths[column] = newval
666
- })
667
-
668
- this.set('widths', widths)
669
- }
670
-
671
- distributeVertical(cells: Component[]) {}
672
- }
673
-
674
- Component.register('data-list', DataList)