@operato/scene-storage 10.0.0-beta.33 → 10.0.0-beta.34

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 (89) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/crane-3d.d.ts +14 -0
  3. package/dist/crane-3d.js +238 -0
  4. package/dist/crane-3d.js.map +1 -0
  5. package/dist/crane.d.ts +157 -0
  6. package/dist/crane.js +440 -0
  7. package/dist/crane.js.map +1 -0
  8. package/dist/index.d.ts +13 -6
  9. package/dist/index.js +9 -4
  10. package/dist/index.js.map +1 -1
  11. package/dist/mobile-storage-rack.d.ts +17 -0
  12. package/dist/mobile-storage-rack.js +55 -0
  13. package/dist/mobile-storage-rack.js.map +1 -0
  14. package/dist/rack-column.d.ts +35 -0
  15. package/dist/rack-column.js +258 -0
  16. package/dist/rack-column.js.map +1 -0
  17. package/dist/rack-grid-3d.d.ts +13 -0
  18. package/dist/rack-grid-3d.js +94 -0
  19. package/dist/rack-grid-3d.js.map +1 -0
  20. package/dist/rack-grid-cell.d.ts +341 -0
  21. package/dist/rack-grid-cell.js +321 -0
  22. package/dist/rack-grid-cell.js.map +1 -0
  23. package/dist/rack-grid-helpers.d.ts +28 -0
  24. package/dist/rack-grid-helpers.js +71 -0
  25. package/dist/rack-grid-helpers.js.map +1 -0
  26. package/dist/rack-grid-location.d.ts +37 -0
  27. package/dist/rack-grid-location.js +227 -0
  28. package/dist/rack-grid-location.js.map +1 -0
  29. package/dist/rack-grid.d.ts +80 -0
  30. package/dist/rack-grid.js +829 -0
  31. package/dist/rack-grid.js.map +1 -0
  32. package/dist/stock.d.ts +78 -0
  33. package/dist/stock.js +333 -0
  34. package/dist/stock.js.map +1 -0
  35. package/dist/{rack-cell-3d.d.ts → storage-cell-3d.d.ts} +1 -1
  36. package/dist/{rack-cell-3d.js → storage-cell-3d.js} +3 -3
  37. package/dist/storage-cell-3d.js.map +1 -0
  38. package/dist/{rack-cell.d.ts → storage-cell.d.ts} +12 -6
  39. package/dist/{rack-cell.js → storage-cell.js} +9 -9
  40. package/dist/storage-cell.js.map +1 -0
  41. package/dist/{asrs-rack-3d.d.ts → storage-rack-3d.d.ts} +1 -1
  42. package/dist/{asrs-rack-3d.js → storage-rack-3d.js} +4 -4
  43. package/dist/storage-rack-3d.js.map +1 -0
  44. package/dist/{asrs-rack.d.ts → storage-rack.d.ts} +22 -16
  45. package/dist/{asrs-rack.js → storage-rack.js} +32 -26
  46. package/dist/storage-rack.js.map +1 -0
  47. package/dist/templates/index.d.ts +60 -0
  48. package/dist/templates/index.js +59 -17
  49. package/dist/templates/index.js.map +1 -1
  50. package/package.json +2 -2
  51. package/src/crane-3d.ts +273 -0
  52. package/src/crane.ts +538 -0
  53. package/src/index.ts +13 -6
  54. package/src/mobile-storage-rack.ts +56 -0
  55. package/src/rack-column.ts +340 -0
  56. package/src/rack-grid-3d.ts +128 -0
  57. package/src/rack-grid-cell.ts +404 -0
  58. package/src/rack-grid-helpers.ts +77 -0
  59. package/src/rack-grid-location.ts +286 -0
  60. package/src/rack-grid.ts +994 -0
  61. package/src/stock.ts +426 -0
  62. package/src/{rack-cell-3d.ts → storage-cell-3d.ts} +2 -2
  63. package/src/{rack-cell.ts → storage-cell.ts} +19 -13
  64. package/src/{asrs-rack-3d.ts → storage-rack-3d.ts} +3 -3
  65. package/src/{asrs-rack.ts → storage-rack.ts} +31 -25
  66. package/src/templates/index.ts +59 -17
  67. package/test/test-rack-grid-crane.ts +212 -0
  68. package/test/test-rack-grid.ts +77 -0
  69. package/test/{test-asrs-crane.ts → test-storage-rack-crane.ts} +8 -8
  70. package/translations/en.json +55 -7
  71. package/translations/ja.json +52 -4
  72. package/translations/ko.json +52 -4
  73. package/translations/ms.json +55 -7
  74. package/translations/zh.json +52 -4
  75. package/tsconfig.tsbuildinfo +1 -1
  76. package/dist/asrs-crane-3d.d.ts +0 -17
  77. package/dist/asrs-crane-3d.js +0 -181
  78. package/dist/asrs-crane-3d.js.map +0 -1
  79. package/dist/asrs-crane.d.ts +0 -98
  80. package/dist/asrs-crane.js +0 -216
  81. package/dist/asrs-crane.js.map +0 -1
  82. package/dist/asrs-rack-3d.js.map +0 -1
  83. package/dist/asrs-rack.js.map +0 -1
  84. package/dist/rack-cell-3d.js.map +0 -1
  85. package/dist/rack-cell.js.map +0 -1
  86. package/src/asrs-crane-3d.ts +0 -211
  87. package/src/asrs-crane.ts +0 -275
  88. /package/icons/{asrs-crane.png → crane.png} +0 -0
  89. /package/icons/{asrs-rack.png → storage-rack.png} +0 -0
@@ -0,0 +1,994 @@
1
+ /*
2
+ * Copyright © HatioLab Inc. All rights reserved.
3
+ */
4
+
5
+ import {
6
+ ApplicationContext,
7
+ Component,
8
+ ComponentNature,
9
+ ContainerAbstract,
10
+ Control,
11
+ Layout,
12
+ Model,
13
+ POINT,
14
+ Properties,
15
+ sceneComponent,
16
+ RealObject
17
+ } from '@hatiolab/things-scene'
18
+ import type { State } from '@hatiolab/things-scene'
19
+ import * as THREE from 'three'
20
+
21
+ /** RackGrid 컴포넌트 state */
22
+ export interface RackGridState extends State {
23
+ rows?: number
24
+ columns?: number
25
+ zone?: string
26
+ shelves?: number
27
+ locPattern?: string
28
+ sectionDigits?: number
29
+ unitDigits?: number
30
+ shelfLocations?: string
31
+ stockScale?: number
32
+ hideRackFrame?: boolean
33
+ legendTarget?: string
34
+ hideEmptyStock?: boolean
35
+ widths?: number[]
36
+ heights?: number[]
37
+ }
38
+ import { RackGridCell } from './rack-grid-cell.js'
39
+ import { RackGrid3D } from './rack-grid-3d.js'
40
+ import { increaseLocation } from './rack-grid-location.js'
41
+ import type { StockMaterialProvider } from './stock.js'
42
+
43
+ const NATURE: ComponentNature = {
44
+ mutable: false,
45
+ resizable: true,
46
+ rotatable: true,
47
+ properties: [
48
+ {
49
+ type: 'number',
50
+ label: 'rows',
51
+ name: 'rows',
52
+ property: 'rows'
53
+ },
54
+ {
55
+ type: 'number',
56
+ label: 'columns',
57
+ name: 'columns',
58
+ property: 'columns'
59
+ },
60
+ {
61
+ type: 'string',
62
+ label: 'zone',
63
+ name: 'zone',
64
+ property: 'zone'
65
+ },
66
+ {
67
+ type: 'number',
68
+ label: 'shelves',
69
+ name: 'shelves',
70
+ property: 'shelves'
71
+ },
72
+ {
73
+ type: 'number',
74
+ label: 'depth',
75
+ name: 'depth',
76
+ property: 'depth'
77
+ },
78
+ {
79
+ type: 'string',
80
+ label: 'location-pattern',
81
+ name: 'locPattern',
82
+ placeholder: '{z}{s}-{u}-{sh}'
83
+ },
84
+ {
85
+ type: 'number',
86
+ label: 'section-digits',
87
+ name: 'sectionDigits',
88
+ placeholder: '1, 2, 3, ...'
89
+ },
90
+ {
91
+ type: 'number',
92
+ label: 'unit-digits',
93
+ name: 'unitDigits',
94
+ placeholder: '1, 2, 3, ...'
95
+ },
96
+ {
97
+ type: 'string',
98
+ label: 'shelf-locations',
99
+ name: 'shelfLocations',
100
+ placeholder: '1,2,3,... / ,,,04'
101
+ },
102
+ {
103
+ type: 'number',
104
+ label: 'stock-scale',
105
+ name: 'stockScale',
106
+ property: {
107
+ step: 0.01,
108
+ min: 0,
109
+ max: 1
110
+ }
111
+ },
112
+ {
113
+ type: 'checkbox',
114
+ label: 'hide-rack-frame',
115
+ name: 'hideRackFrame'
116
+ },
117
+ {
118
+ type: 'id-input',
119
+ label: 'legend-target',
120
+ name: 'legendTarget',
121
+ property: {
122
+ component: 'legend'
123
+ }
124
+ },
125
+ {
126
+ type: 'checkbox',
127
+ label: 'hide-empty-stock',
128
+ name: 'hideEmptyStock'
129
+ }
130
+ ],
131
+ help: 'scene/component/rack-grid'
132
+ }
133
+
134
+ type SIDE_KEY = 'all' | 'out' | 'left' | 'right' | 'top' | 'bottom' | 'leftright' | 'topbottom'
135
+
136
+ const SIDES = {
137
+ all: ['top', 'left', 'bottom', 'right'],
138
+ out: ['top', 'left', 'bottom', 'right'],
139
+ left: ['left'],
140
+ right: ['right'],
141
+ top: ['top'],
142
+ bottom: ['bottom'],
143
+ leftright: ['left', 'right'],
144
+ topbottom: ['top', 'bottom']
145
+ } as { [key: string]: SIDE_KEY[] }
146
+
147
+ const CLEAR_STYLE = {
148
+ strokeStyle: '',
149
+ lineDash: 'solid',
150
+ lineWidth: 0
151
+ }
152
+
153
+ const DEFAULT_STYLE = {
154
+ strokeStyle: '#999',
155
+ lineDash: 'solid',
156
+ lineWidth: 1
157
+ }
158
+
159
+ const TABLE_LAYOUT = Layout.get('table')
160
+
161
+ function buildNewCell(app: ApplicationContext) {
162
+ return Model.compile(
163
+ {
164
+ type: 'rack-grid-cell',
165
+ strokeStyle: 'black',
166
+ fillStyle: 'transparent',
167
+ left: 0,
168
+ top: 0,
169
+ width: 1,
170
+ height: 1,
171
+ textWrap: true,
172
+ isEmpty: false,
173
+ border: buildBorderStyle(DEFAULT_STYLE, 'all')
174
+ },
175
+ app
176
+ )
177
+ }
178
+
179
+ function buildCopiedCell(copy: any, app: ApplicationContext) {
180
+ const obj = JSON.parse(JSON.stringify(copy))
181
+ delete obj.text
182
+
183
+ return Model.compile(obj, app)
184
+ }
185
+
186
+ function buildBorderStyle(style: any, where: SIDE_KEY): { [key: string]: any } {
187
+ return (SIDES[where] || []).reduce((border: { [key: string]: any }, side: string) => {
188
+ border[side] = style
189
+ return border
190
+ }, {} as { [key: string]: any })
191
+ }
192
+
193
+ function setCellBorder(cell: RackGridCell, style: any, where: SIDE_KEY) {
194
+ if (!cell) {
195
+ return
196
+ }
197
+
198
+ cell.set('border', Object.assign({}, cell.getState('border') || {}, buildBorderStyle(style, where)))
199
+ }
200
+
201
+ function isLeftMost(total: number, columns: number, indices: number[], i: number) {
202
+ return i == 0 || !(i % columns) || indices.indexOf(i - 1) == -1
203
+ }
204
+
205
+ function isRightMost(total: number, columns: number, indices: number[], i: number) {
206
+ return i == total - 1 || i % columns == columns - 1 || indices.indexOf(i + 1) == -1
207
+ }
208
+
209
+ function isTopMost(total: number, columns: number, indices: number[], i: number) {
210
+ return i < columns || indices.indexOf(i - columns) == -1
211
+ }
212
+
213
+ function isBottomMost(total: number, columns: number, indices: number[], i: number) {
214
+ return i > total - columns - 1 || indices.indexOf(i + columns) == -1
215
+ }
216
+
217
+ function above(columns: number, i: number) {
218
+ return i - columns
219
+ }
220
+
221
+ function below(columns: number, i: number) {
222
+ return i + columns
223
+ }
224
+
225
+ function before(columns: number, i: number) {
226
+ return !(i % columns) ? -1 : i - 1
227
+ }
228
+
229
+ function after(columns: number, i: number) {
230
+ return !((i + 1) % columns) ? -1 : i + 1
231
+ }
232
+
233
+ function array(value: any, size: number) {
234
+ const arr = []
235
+ for (let i = 0; i < size; i++) arr.push(1)
236
+ return arr
237
+ }
238
+
239
+ const columnControlHandler = {
240
+ ondragmove: function (point: POINT, index: number, component: RackGrid) {
241
+ const { left, top, width, height } = component.textBounds
242
+ const widths_sum = component.widths_sum
243
+
244
+ const widths = component.widths.slice()
245
+
246
+ /* 컨트롤의 원래 위치를 구한다. */
247
+ const origin_pos_unit = widths.slice(0, index + 1).reduce((sum: number, width: number) => sum + width, 0)
248
+ const origin_offset = left + (origin_pos_unit / widths_sum) * width
249
+
250
+ /*
251
+ * point의 좌표는 부모 레이어 기준의 x, y 값이다.
252
+ * 따라서, 도형의 회전을 감안한 좌표로의 변환이 필요하다.
253
+ * Transcoord시에는 point좌표가 부모까지 transcoord되어있는 상태이므로,
254
+ * 컴포넌트자신에 대한 transcoord만 필요하다.(마지막 파라미터를 false로).
255
+ */
256
+ const transcoorded = component.transcoordP2S(point.x, point.y)
257
+ const diff = transcoorded.x - origin_offset
258
+
259
+ let diff_unit = (diff / width) * widths_sum
260
+
261
+ const min_width_unit = (widths_sum / width) * 5 // 5픽셀정도를 최소로
262
+
263
+ if (diff_unit < 0) diff_unit = -Math.min(widths[index] - min_width_unit, -diff_unit)
264
+ else diff_unit = Math.min(widths[index + 1] - min_width_unit, diff_unit)
265
+
266
+ widths[index] = Math.round((widths[index] + diff_unit) * 100) / 100
267
+ widths[index + 1] = Math.round((widths[index + 1] - diff_unit) * 100) / 100
268
+
269
+ component.set('widths', widths)
270
+ }
271
+ }
272
+
273
+ const rowControlHandler = {
274
+ ondragmove: function (point: POINT, index: number, component: RackGrid) {
275
+ const { left, top, width, height } = component.textBounds
276
+ const heights_sum = component.heights_sum
277
+
278
+ const heights = component.heights.slice()
279
+
280
+ /* 컨트롤의 원래 위치를 구한다. */
281
+ index -= component.columns - 1
282
+ const origin_pos_unit = heights.slice(0, index + 1).reduce((sum: number, height: number) => sum + height, 0)
283
+ const origin_offset = top + (origin_pos_unit / heights_sum) * height
284
+
285
+ /*
286
+ * point의 좌표는 부모 레이어 기준의 x, y 값이다.
287
+ * 따라서, 도형의 회전을 감안한 좌표로의 변환이 필요하다.
288
+ * Transcoord시에는 point좌표가 부모까지 transcoord되어있는 상태이므로,
289
+ * 컴포넌트자신에 대한 transcoord만 필요하다.(마지막 파라미터를 false로).
290
+ */
291
+ const transcoorded = component.transcoordP2S(point.x, point.y)
292
+ const diff = transcoorded.y - origin_offset
293
+
294
+ let diff_unit = (diff / height) * heights_sum
295
+
296
+ const min_height_unit = (heights_sum / height) * 5 // 5픽셀정도를 최소로
297
+
298
+ if (diff_unit < 0) diff_unit = -Math.min(heights[index] - min_height_unit, -diff_unit)
299
+ else diff_unit = Math.min(heights[index + 1] - min_height_unit, diff_unit)
300
+
301
+ heights[index] = Math.round((heights[index] + diff_unit) * 100) / 100
302
+ heights[index + 1] = Math.round((heights[index + 1] - diff_unit) * 100) / 100
303
+
304
+ component.set('heights', heights)
305
+ }
306
+ }
307
+
308
+ @sceneComponent('rack-grid')
309
+ export default class RackGrid extends ContainerAbstract implements StockMaterialProvider {
310
+ override get state(): RackGridState {
311
+ return super.state as RackGridState
312
+ }
313
+
314
+ _focused_cell?: RackGridCell
315
+
316
+ // StockMaterialProvider 구현
317
+ _stock_materials: THREE.Material[] = []
318
+ _default_material?: THREE.Material
319
+ _empty_material?: THREE.Material
320
+ private _legendTarget?: Component
321
+
322
+ get legendTarget(): Component | undefined {
323
+ const { legendTarget } = this.state
324
+
325
+ if (!this._legendTarget && legendTarget) {
326
+ this._legendTarget = this.root.findById?.(legendTarget)
327
+ this._legendTarget?.on('change', this._onLegendChanged, this)
328
+ }
329
+
330
+ // 하위호환: 자체 legendTarget이 없으면 부모(Visualizer)의 legendTarget 폴백
331
+ if (!this._legendTarget) {
332
+ let ancestor = this.parent as any
333
+ while (ancestor) {
334
+ if (ancestor.legendTarget) return ancestor.legendTarget
335
+ ancestor = ancestor.parent
336
+ }
337
+ // 서비스 레지스트리 폴백: stock-hub의 legendTarget
338
+ const stockHub = (this.root as any)?.getService?.('stock')
339
+ if (stockHub?.legendTarget) return stockHub.legendTarget
340
+ }
341
+
342
+ return this._legendTarget
343
+ }
344
+
345
+ get hideEmptyStock(): boolean {
346
+ return !!this.getState('hideEmptyStock')
347
+ }
348
+
349
+ private _onLegendChanged() {
350
+ this._resetMaterials()
351
+ this.invalidate()
352
+ }
353
+
354
+ private _resetMaterials() {
355
+ this._stock_materials.forEach(m => m.dispose?.())
356
+ this._stock_materials = []
357
+ delete this._default_material
358
+ delete this._empty_material
359
+ }
360
+ buildRealObject(): RealObject | undefined {
361
+ return new RackGrid3D(this)
362
+ }
363
+
364
+ dispose() {
365
+ this._legendTarget?.off('change', this._onLegendChanged, this)
366
+ delete this._legendTarget
367
+ this._resetMaterials()
368
+
369
+ super.dispose()
370
+
371
+ delete this._focused_cell
372
+ }
373
+
374
+ created() {
375
+ const tobeSize = this.rows * this.columns
376
+ const gap = this.size() - tobeSize
377
+
378
+ if (gap == 0) {
379
+ return
380
+ } else if (gap > 0) {
381
+ let removals = this.components.slice(gap)
382
+ this.remove(removals)
383
+ } else {
384
+ let newbies = []
385
+
386
+ for (let i = 0; i < -gap; i++) newbies.push(buildNewCell(this.app))
387
+
388
+ this.add(newbies)
389
+ }
390
+
391
+ const widths = this.getState('widths')
392
+ const heights = this.getState('heights')
393
+
394
+ if (!widths || widths.length < this.columns) this.set('widths', this.widths)
395
+ if (!heights || heights.length < this.rows) this.set('heights', this.heights)
396
+ }
397
+
398
+ // 컴포넌트를 임의로 추가 및 삭제할 수 있는 지를 지정하는 속성임.
399
+ get focusible() {
400
+ return false
401
+ }
402
+
403
+ get widths(): number[] {
404
+ const widths = this.getState('widths')
405
+
406
+ if (!widths) return array(1, this.columns)
407
+
408
+ if (widths.length < this.columns) return widths.concat(array(1, this.columns - widths.length))
409
+ else if (widths.length > this.columns) return widths.slice(0, this.columns)
410
+
411
+ return widths
412
+ }
413
+
414
+ get heights(): number[] {
415
+ const heights = this.getState('heights')
416
+
417
+ if (!heights) return array(1, this.rows)
418
+
419
+ if (heights.length < this.rows) return heights.concat(array(1, this.rows - heights.length))
420
+ else if (heights.length > this.rows) return heights.slice(0, this.rows)
421
+
422
+ return heights
423
+ }
424
+
425
+ buildCells(newrows: number, newcolumns: number, oldrows: number, oldcolumns: number) {
426
+ if (newrows < oldrows) {
427
+ let removals = this.components.slice(oldcolumns * newrows)
428
+ this.remove(removals)
429
+ }
430
+
431
+ const minrows = Math.min(newrows, oldrows)
432
+
433
+ if (newcolumns > oldcolumns) {
434
+ for (let r = 0; r < minrows; r++) {
435
+ for (let c = oldcolumns; c < newcolumns; c++) {
436
+ this.insertComponentAt(buildNewCell(this.app), r * newcolumns + c)
437
+ }
438
+ }
439
+ } else if (newcolumns < oldcolumns) {
440
+ let removals = []
441
+
442
+ for (let r = 0; r < minrows; r++) {
443
+ for (let c = newcolumns; c < oldcolumns; c++) {
444
+ removals.push(this.components[r * oldcolumns + c])
445
+ }
446
+ }
447
+ this.remove(removals)
448
+ }
449
+
450
+ if (newrows > oldrows) {
451
+ let newbies = []
452
+
453
+ for (let r = oldrows; r < newrows; r++) {
454
+ for (let i = 0; i < newcolumns; i++) {
455
+ newbies.push(buildNewCell(this.app))
456
+ }
457
+ }
458
+ this.add(newbies)
459
+ }
460
+
461
+ this.set({
462
+ widths: this.widths,
463
+ heights: this.heights
464
+ })
465
+ }
466
+
467
+ get layout(): any {
468
+ return TABLE_LAYOUT
469
+ }
470
+
471
+ get rows() {
472
+ return this.getState('rows')
473
+ }
474
+
475
+ setCellsStyle(cells: RackGridCell[], style: any, where: string) {
476
+ const components = this.components
477
+ const total = components.length
478
+ const columns = this.getState('columns')
479
+
480
+ // 병합된 셀도 포함시킨다.
481
+ const _cells = [] as RackGridCell[]
482
+ cells.forEach(c => {
483
+ _cells.push(c)
484
+ if (c.colspan || c.rowspan) {
485
+ let col = this.getRowColumn(c).column
486
+ let row = this.getRowColumn(c).row
487
+ for (let i = row; i < row + c.rowspan; i++)
488
+ for (let j = col; j < col + c.colspan; j++)
489
+ if (i != row || j != col) _cells.push(this.components[i * this.columns + j] as RackGridCell)
490
+ }
491
+ })
492
+
493
+ const indices = _cells.map(cell => components.indexOf(cell))
494
+ indices.forEach(i => {
495
+ const cell = components[i] as RackGridCell
496
+
497
+ switch (where) {
498
+ case 'all':
499
+ setCellBorder(cell, style, where)
500
+
501
+ if (isLeftMost(total, columns, indices, i))
502
+ setCellBorder(components[before(columns, i)] as RackGridCell, style, 'right')
503
+ if (isRightMost(total, columns, indices, i))
504
+ setCellBorder(components[after(columns, i)] as RackGridCell, style, 'left')
505
+ if (isTopMost(total, columns, indices, i))
506
+ setCellBorder(components[above(columns, i)] as RackGridCell, style, 'bottom')
507
+ if (isBottomMost(total, columns, indices, i))
508
+ setCellBorder(components[below(columns, i)] as RackGridCell, style, 'top')
509
+ break
510
+ case 'in':
511
+ if (!isLeftMost(total, columns, indices, i)) {
512
+ setCellBorder(cell, style, 'left')
513
+ }
514
+ if (!isRightMost(total, columns, indices, i)) {
515
+ setCellBorder(cell, style, 'right')
516
+ }
517
+ if (!isTopMost(total, columns, indices, i)) {
518
+ setCellBorder(cell, style, 'top')
519
+ }
520
+ if (!isBottomMost(total, columns, indices, i)) {
521
+ setCellBorder(cell, style, 'bottom')
522
+ }
523
+ break
524
+ case 'out':
525
+ if (isLeftMost(total, columns, indices, i)) {
526
+ setCellBorder(cell, style, 'left')
527
+ setCellBorder(components[before(columns, i)] as RackGridCell, style, 'right')
528
+ }
529
+ if (isRightMost(total, columns, indices, i)) {
530
+ setCellBorder(cell, style, 'right')
531
+ setCellBorder(components[after(columns, i)] as RackGridCell, style, 'left')
532
+ }
533
+ if (isTopMost(total, columns, indices, i)) {
534
+ setCellBorder(cell, style, 'top')
535
+ setCellBorder(components[above(columns, i)] as RackGridCell, style, 'bottom')
536
+ }
537
+ if (isBottomMost(total, columns, indices, i)) {
538
+ setCellBorder(cell, style, 'bottom')
539
+ setCellBorder(components[below(columns, i)] as RackGridCell, style, 'top')
540
+ }
541
+ break
542
+ case 'left':
543
+ if (isLeftMost(total, columns, indices, i)) {
544
+ setCellBorder(cell, style, 'left')
545
+ setCellBorder(components[before(columns, i)] as RackGridCell, style, 'right')
546
+ }
547
+ break
548
+ case 'right':
549
+ if (isRightMost(total, columns, indices, i)) {
550
+ setCellBorder(cell, style, 'right')
551
+ setCellBorder(components[after(columns, i)] as RackGridCell, style, 'left')
552
+ }
553
+ break
554
+ case 'center':
555
+ if (!isLeftMost(total, columns, indices, i)) {
556
+ setCellBorder(cell, style, 'left')
557
+ }
558
+ if (!isRightMost(total, columns, indices, i)) {
559
+ setCellBorder(cell, style, 'right')
560
+ }
561
+ break
562
+ case 'middle':
563
+ if (!isTopMost(total, columns, indices, i)) {
564
+ setCellBorder(cell, style, 'top')
565
+ }
566
+ if (!isBottomMost(total, columns, indices, i)) {
567
+ setCellBorder(cell, style, 'bottom')
568
+ }
569
+ break
570
+ case 'top':
571
+ if (isTopMost(total, columns, indices, i)) {
572
+ setCellBorder(cell, style, 'top')
573
+ setCellBorder(components[above(columns, i)] as RackGridCell, style, 'bottom')
574
+ }
575
+ break
576
+ case 'bottom':
577
+ if (isBottomMost(total, columns, indices, i)) {
578
+ setCellBorder(cell, style, 'bottom')
579
+ setCellBorder(components[below(columns, i)] as RackGridCell, style, 'top')
580
+ }
581
+ break
582
+ case 'clear':
583
+ setCellBorder(cell, CLEAR_STYLE, 'all')
584
+
585
+ if (isLeftMost(total, columns, indices, i))
586
+ setCellBorder(components[before(columns, i)] as RackGridCell, CLEAR_STYLE, 'right')
587
+ if (isRightMost(total, columns, indices, i))
588
+ setCellBorder(components[after(columns, i)] as RackGridCell, CLEAR_STYLE, 'left')
589
+ if (isTopMost(total, columns, indices, i))
590
+ setCellBorder(components[above(columns, i)] as RackGridCell, CLEAR_STYLE, 'bottom')
591
+ if (isBottomMost(total, columns, indices, i))
592
+ setCellBorder(components[below(columns, i)] as RackGridCell, CLEAR_STYLE, 'top')
593
+ }
594
+ })
595
+ }
596
+
597
+ getRowColumn(cell: RackGridCell) {
598
+ const idx = this.components.indexOf(cell)
599
+
600
+ return {
601
+ column: idx % this.columns,
602
+ row: Math.floor(idx / this.columns)
603
+ }
604
+ }
605
+
606
+ getCellsByRow(row: number) {
607
+ return this.components.slice(row * this.columns, (row + 1) * this.columns)
608
+ }
609
+
610
+ getCellsByColumn(column: number) {
611
+ const cells = []
612
+ for (let i = 0; i < this.rows; i++) cells.push(this.components[this.columns * i + column])
613
+
614
+ return cells
615
+ }
616
+
617
+ deleteRows(cells: RackGridCell[]) {
618
+ // 먼저 cells 위치의 행을 구한다.
619
+ let rows = [] as number[]
620
+
621
+ cells.forEach(cell => {
622
+ let row = this.getRowColumn(cell).row
623
+ if (-1 == rows.indexOf(row)) {
624
+ rows.push(row)
625
+ }
626
+ })
627
+
628
+ rows.sort((a, b) => {
629
+ return a - b
630
+ })
631
+
632
+ rows.reverse()
633
+
634
+ const heights = this.heights.slice()
635
+ rows.forEach(row => {
636
+ this.remove(this.getCellsByRow(row))
637
+ })
638
+
639
+ rows.forEach(row => heights.splice(row, 1))
640
+
641
+ this.model.rows -= rows.length // 고의적으로, change 이벤트가 발생하지 않도록 set(..)을 사용하지 않음.
642
+ this.set('heights', heights)
643
+ }
644
+
645
+ deleteColumns(cells: RackGridCell[]) {
646
+ // 먼저 cells 위치의 열을 구한다.
647
+ let columns = [] as number[]
648
+ cells.forEach(cell => {
649
+ let column = this.getRowColumn(cell).column
650
+ if (-1 == columns.indexOf(column)) columns.push(column)
651
+ })
652
+ columns.sort((a, b) => {
653
+ return a - b
654
+ })
655
+ columns.reverse()
656
+
657
+ columns.forEach(column => {
658
+ const widths = this.widths.slice()
659
+ this.remove(this.getCellsByColumn(column))
660
+ widths.splice(column, 1)
661
+ this.model.columns -= 1 // 고의적으로, change 이벤트가 발생하지 않도록 set(..)을 사용하지 않음.
662
+ this.set('widths', widths)
663
+ })
664
+ }
665
+
666
+ insertCellsAbove(cells: RackGridCell[]) {
667
+ // 먼저 cells 위치의 행을 구한다.
668
+ let rows = [] as number[]
669
+ cells.forEach(cell => {
670
+ let row = this.getRowColumn(cell).row
671
+ if (-1 == rows.indexOf(row)) rows.push(row)
672
+ })
673
+ rows.sort((a, b) => {
674
+ return a - b
675
+ })
676
+ rows.reverse()
677
+ // 행 2개 이상은 추가 안함. 임시로 막아놓음
678
+ if (rows.length >= 2) return false
679
+ let insertionRowPosition = rows[0]
680
+ let newbieRowHeights = [] as number[]
681
+ let newbieCells = [] as RackGridCell[]
682
+ rows.forEach(row => {
683
+ for (let i = 0; i < this.columns; i++)
684
+ newbieCells.push(buildCopiedCell(this.components[row * this.columns + i].model, this.app) as RackGridCell)
685
+
686
+ newbieRowHeights.push(this.heights[row])
687
+
688
+ newbieCells.reverse().forEach(cell => {
689
+ this.insertComponentAt(cell, insertionRowPosition * this.columns)
690
+ })
691
+
692
+ let heights = this.heights.slice()
693
+ heights.splice(insertionRowPosition, 0, ...newbieRowHeights)
694
+ this.set('heights', heights)
695
+
696
+ this.model.rows += rows.length
697
+
698
+ this.clearCache()
699
+ })
700
+ }
701
+
702
+ insertCellsBelow(cells: RackGridCell[]) {
703
+ // 먼저 cells 위치의 행을 구한다.
704
+ let rows = [] as number[]
705
+ cells.forEach(cell => {
706
+ let row = this.getRowColumn(cell).row
707
+ if (-1 == rows.indexOf(row)) rows.push(row)
708
+ })
709
+ rows.sort((a, b) => {
710
+ return a - b
711
+ })
712
+ rows.reverse()
713
+ // 행 2개 이상은 추가 안함. 임시로 막아놓음
714
+ if (rows.length >= 2) return false
715
+ let insertionRowPosition = rows[rows.length - 1] + 1
716
+ let newbieRowHeights = [] as number[]
717
+ let newbieCells = [] as RackGridCell[]
718
+ rows.forEach(row => {
719
+ for (let i = 0; i < this.columns; i++)
720
+ newbieCells.push(buildCopiedCell(this.components[row * this.columns + i].model, this.app) as RackGridCell)
721
+ newbieRowHeights.push(this.heights[row])
722
+
723
+ newbieCells.reverse().forEach(cell => {
724
+ this.insertComponentAt(cell, insertionRowPosition * this.columns)
725
+ })
726
+
727
+ let heights = this.heights.slice()
728
+ heights.splice(insertionRowPosition, 0, ...newbieRowHeights)
729
+ this.set('heights', heights)
730
+
731
+ this.model.rows += 1
732
+
733
+ this.clearCache()
734
+ })
735
+ }
736
+
737
+ insertCellsLeft(cells: RackGridCell[]) {
738
+ // 먼저 cells 위치의 열을 구한다.
739
+ let columns = [] as number[]
740
+ cells.forEach(cell => {
741
+ let column = this.getRowColumn(cell).column
742
+ if (-1 == columns.indexOf(column)) columns.push(column)
743
+ })
744
+ columns.sort((a, b) => {
745
+ return a - b
746
+ })
747
+ columns.reverse()
748
+ // 열 2개 이상은 추가 안함. 임시로 막아놓음
749
+ if (columns.length >= 2) return false
750
+ let insertionColumnPosition = columns[0]
751
+ let newbieColumnWidths = [] as number[]
752
+ let newbieCells = [] as RackGridCell[]
753
+ columns.forEach(column => {
754
+ for (let i = 0; i < this.rows; i++)
755
+ newbieCells.push(buildCopiedCell(this.components[column + this.columns * i].model, this.app) as RackGridCell)
756
+ newbieColumnWidths.push(this.widths[column])
757
+
758
+ let increasedColumns = this.columns
759
+ let index = this.rows
760
+ newbieCells.reverse().forEach(cell => {
761
+ if (index == 0) {
762
+ index = this.rows
763
+ increasedColumns++
764
+ }
765
+
766
+ index--
767
+ this.insertComponentAt(cell, insertionColumnPosition + index * increasedColumns)
768
+ })
769
+
770
+ let widths = this.widths.slice()
771
+ this.model.columns += columns.length // 고의적으로, change 이벤트가 발생하지 않도록 set(..)을 사용하지 않음.
772
+
773
+ widths.splice(insertionColumnPosition, 0, ...newbieColumnWidths)
774
+
775
+ this.set('widths', widths)
776
+ })
777
+ }
778
+
779
+ insertCellsRight(cells: RackGridCell[]) {
780
+ // 먼저 cells 위치의 열을 구한다.
781
+ let columns = [] as number[]
782
+ cells.forEach(cell => {
783
+ let column = this.getRowColumn(cell).column
784
+ if (-1 == columns.indexOf(column)) columns.push(column)
785
+ })
786
+ columns.sort((a, b) => {
787
+ return a - b
788
+ })
789
+ columns.reverse()
790
+ // 열 2개 이상은 추가 안함. 임시로 막아놓음
791
+ if (columns.length >= 2) return false
792
+ let insertionColumnPosition = columns[columns.length - 1] + 1
793
+ let newbieColumnWidths = [] as number[]
794
+ let newbieCells = [] as RackGridCell[]
795
+ columns.forEach(column => {
796
+ for (let i = 0; i < this.rows; i++)
797
+ newbieCells.push(buildCopiedCell(this.components[column + this.columns * i].model, this.app) as RackGridCell)
798
+ newbieColumnWidths.push(this.widths[column])
799
+
800
+ let increasedColumns = this.columns
801
+ let index = this.rows
802
+ newbieCells.reverse().forEach(cell => {
803
+ if (index == 0) {
804
+ index = this.rows
805
+ increasedColumns++
806
+ }
807
+
808
+ index--
809
+ this.insertComponentAt(cell, insertionColumnPosition + index * increasedColumns)
810
+ })
811
+
812
+ let widths = this.widths.slice()
813
+ this.model.columns += columns.length // 고의적으로, change 이벤트가 발생하지 않도록 set(..)을 사용하지 않음.
814
+
815
+ widths.splice(insertionColumnPosition, 0, ...newbieColumnWidths)
816
+
817
+ this.set('widths', widths)
818
+ })
819
+ }
820
+
821
+ distributeHorizontal(cells: RackGridCell[]) {
822
+ const columns = [] as number[]
823
+
824
+ cells.forEach(cell => {
825
+ let rowcolumn = this.getRowColumn(cell)
826
+
827
+ if (-1 == columns.indexOf(rowcolumn.column)) columns.push(rowcolumn.column)
828
+ })
829
+
830
+ const sum = columns.reduce((sum, column) => {
831
+ return sum + this.widths[column]
832
+ }, 0)
833
+
834
+ const newval = Math.round((sum / columns.length) * 100) / 100
835
+ const widths = this.widths.slice()
836
+ columns.forEach(column => {
837
+ widths[column] = newval
838
+ })
839
+
840
+ this.set('widths', widths)
841
+ }
842
+
843
+ distributeVertical(cells: RackGridCell[]) {
844
+ const rows = [] as number[]
845
+
846
+ cells.forEach(cell => {
847
+ let rowcolumn = this.getRowColumn(cell)
848
+
849
+ if (-1 == rows.indexOf(rowcolumn.row)) rows.push(rowcolumn.row)
850
+ })
851
+
852
+ const sum = rows.reduce((sum, row) => {
853
+ return sum + this.heights[row]
854
+ }, 0)
855
+
856
+ const newval = Math.round((sum / rows.length) * 100) / 100
857
+ const heights = this.heights.slice()
858
+ rows.forEach(row => {
859
+ heights[row] = newval
860
+ })
861
+
862
+ this.set('heights', heights)
863
+ }
864
+
865
+ increaseLocation(type: string, skipNumbering: boolean, startSection: number, startUnit: number) {
866
+ const { sectionDigits = 2, unitDigits = 2 } = this.state
867
+ const selectedCells = this.root.selected as RackGridCell[]
868
+
869
+ increaseLocation(selectedCells, type, skipNumbering, startSection, startUnit, sectionDigits, unitDigits)
870
+ }
871
+
872
+ get columns() {
873
+ return this.getState('columns')
874
+ }
875
+
876
+ get lefts() {
877
+ return this.components.filter((c: any, i: any) => {
878
+ return !(i % this.columns)
879
+ })
880
+ }
881
+
882
+ get centers() {
883
+ return this.components.filter((c: any, i: any) => {
884
+ return i % this.columns && (i + 1) % this.columns
885
+ })
886
+ }
887
+
888
+ get rights() {
889
+ return this.components.filter((c: any, i: any) => {
890
+ return !((i + 1) % this.columns)
891
+ })
892
+ }
893
+
894
+ get tops() {
895
+ return this.components.slice(0, this.columns)
896
+ }
897
+
898
+ get middles() {
899
+ return this.components.slice(this.columns, this.columns * (this.rows - 1))
900
+ }
901
+
902
+ get bottoms() {
903
+ return this.components.slice(this.columns * (this.rows - 1))
904
+ }
905
+
906
+ get all() {
907
+ return this.components
908
+ }
909
+
910
+ get widths_sum() {
911
+ const widths = this.widths
912
+ return widths ? widths.filter((width, i) => i < this.columns).reduce((sum, width) => sum + width, 0) : this.columns
913
+ }
914
+
915
+ get heights_sum() {
916
+ const heights = this.heights
917
+ return heights ? heights.filter((height, i) => i < this.rows).reduce((sum, height) => sum + height, 0) : this.rows
918
+ }
919
+
920
+ get nature() {
921
+ return NATURE
922
+ }
923
+
924
+ get controls(): Array<Control> | undefined {
925
+ const widths = this.widths
926
+ const heights = this.heights
927
+ const inside = this.textBounds
928
+
929
+ const width_unit = inside.width / this.widths_sum
930
+ const height_unit = inside.height / this.heights_sum
931
+
932
+ let x = inside.left
933
+ let y = inside.top
934
+
935
+ const controls: Array<Control> = []
936
+
937
+ widths.slice(0, this.columns - 1).forEach((width: number) => {
938
+ x += width * width_unit
939
+ controls.push({
940
+ x: x,
941
+ y: inside.top,
942
+ handler: columnControlHandler
943
+ })
944
+ })
945
+
946
+ heights.slice(0, this.rows - 1).forEach((height: number) => {
947
+ y += height * height_unit
948
+ controls.push({
949
+ x: inside.left,
950
+ y: y,
951
+ handler: rowControlHandler
952
+ })
953
+ })
954
+
955
+ return controls
956
+ }
957
+
958
+ onchange(after: Properties, before: Properties) {
959
+ if ('legendTarget' in after || 'legendTarget' in before) {
960
+ this._legendTarget?.off('change', this._onLegendChanged, this)
961
+ delete this._legendTarget
962
+ this._resetMaterials()
963
+ }
964
+
965
+ if ('rows' in after || 'columns' in after) {
966
+ this.buildCells(
967
+ this.getState('rows'),
968
+ this.getState('columns'),
969
+ before.hasOwnProperty('rows') ? before.rows : this.getState('rows'),
970
+ before.hasOwnProperty('columns') ? before.columns : this.getState('columns')
971
+ )
972
+ }
973
+
974
+ this.invalidate()
975
+ }
976
+
977
+ get eventMap() {
978
+ return {
979
+ '(self)': {
980
+ '(descendant)': {
981
+ change: this.oncellchanged
982
+ }
983
+ }
984
+ }
985
+ }
986
+
987
+ oncellchanged(after: Properties, before: Properties) {
988
+ this.invalidate()
989
+ }
990
+ }
991
+
992
+ ;['rows', 'columns', 'widths', 'heights', 'widths_sum', 'heights_sum', 'controls'].forEach(getter =>
993
+ Component.memoize(RackGrid.prototype, getter, false)
994
+ )