@operato/scene-storage 10.0.0-beta.32 → 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 +22 -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 +3 -3
  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,404 @@
1
+ /*
2
+ * Copyright © HatioLab Inc. All rights reserved.
3
+ */
4
+ import { Component, Properties, RectPath, sceneComponent, RealObject } from '@hatiolab/things-scene'
5
+ import type { State } from '@hatiolab/things-scene'
6
+ import { Rack } from './rack-column.js'
7
+
8
+ /** RackGridCell 컴포넌트 state */
9
+ export interface RackGridCellState extends State {
10
+ section?: string
11
+ unit?: string
12
+ shelfLocations?: string
13
+ binLocations?: string
14
+ isEmpty?: boolean
15
+ border?: any
16
+ merged?: boolean
17
+ rowspan?: number
18
+ colspan?: number
19
+ }
20
+
21
+ const EMPTY_BORDER = {}
22
+
23
+ function isBottomMost(idx: number, rows: number, columns: number) {
24
+ return idx >= (rows - 1) * columns
25
+ }
26
+
27
+ function isRightMost(idx: number, rows: number, columns: number) {
28
+ return (idx + 1) % columns == 0
29
+ }
30
+
31
+ function hasAnyProperty(o: any, ...properties: string[]) {
32
+ for (let p in properties) {
33
+ if (o.hasOwnProperty(properties[p])) {
34
+ return true
35
+ }
36
+ }
37
+ }
38
+
39
+ const EMPTY_CELL_STROKE_STYLE = '#ccc'
40
+ const EMPTY_CELL_LINE_WIDTH = 1
41
+ const EMPTY_CELL_FILL_STYLE = '#efefef'
42
+
43
+ /**
44
+ * 1. 스타일을 상속 받아야 함. (cascade-style)
45
+ * 2. 스타일을 동적처리할 수 있음. (로직처리)
46
+ * 3. 데이타를 받을 수 있음.
47
+ */
48
+ @sceneComponent('rack-grid-cell')
49
+ export class RackGridCell extends RectPath(Component) {
50
+ override get state(): RackGridCellState {
51
+ return super.state as RackGridCellState
52
+ }
53
+
54
+ _focused: boolean = false
55
+
56
+ get hasTextProperty() {
57
+ return false
58
+ }
59
+
60
+ get nature() {
61
+ return {
62
+ mutable: false,
63
+ resizable: true,
64
+ rotatable: true,
65
+ properties: [
66
+ {
67
+ type: 'string',
68
+ label: 'section',
69
+ name: 'section'
70
+ },
71
+ {
72
+ type: 'string',
73
+ label: 'unit',
74
+ name: 'unit'
75
+ },
76
+ {
77
+ type: 'string',
78
+ label: 'shelf-locations',
79
+ name: 'shelfLocations',
80
+ placeholder: '1,2,3,... / ,,,04'
81
+ },
82
+ {
83
+ type: 'textarea',
84
+ label: 'bin-locations',
85
+ name: 'binLocations',
86
+ placeholder: '1,2,3,...'
87
+ },
88
+ {
89
+ type: 'checkbox',
90
+ label: 'is-empty',
91
+ name: 'isEmpty'
92
+ },
93
+ {
94
+ type: 'location-increase-pattern',
95
+ label: '',
96
+ name: '',
97
+ property: {
98
+ event: {
99
+ 'increase-location-pattern': (event: CustomEvent) => {
100
+ const { increasingDirection, skipNumbering, startSection, startUnit } = event.detail
101
+ const rackTable = this.parent as any
102
+ rackTable.increaseLocation(increasingDirection, skipNumbering, startSection, startUnit)
103
+ }
104
+ }
105
+ }
106
+ },
107
+ {
108
+ type: 'editor-table',
109
+ label: '',
110
+ name: '',
111
+ property: {
112
+ merge: false,
113
+ split: false
114
+ }
115
+ }
116
+ ]
117
+ }
118
+ }
119
+
120
+ buildRealObject(): RealObject | undefined {
121
+ // isEmpty cell 은 3D 에 mesh 를 만들지 않는다. v9 에서는 RackGrid3D.createRacks 가
122
+ // cell._realObject 를 사전 설정하면 3D pipeline 이 자동 buildRealObject 호출을 skip
123
+ // 하던 것을, v10 의 ThreeCapability 도입 이후로는 *항상* 자동 호출됨. RackGrid3D
124
+ // createRacks 의 isEmpty 체크와 이 자동 경로 사이의 불일치로 isEmpty=true cell 도
125
+ // 3D 에 그려지던 regression — 여기 isEmpty 체크 추가로 두 경로 정합.
126
+ if (this.getState('isEmpty')) return undefined
127
+ return new Rack(this)
128
+ }
129
+
130
+ set merged(merged) {
131
+ this.set('merged', !!merged)
132
+ if (merged) this.set('text', '')
133
+ }
134
+
135
+ get merged() {
136
+ return this.getState('merged')
137
+ }
138
+
139
+ set rowspan(rowspan) {
140
+ this.set('rowspan', rowspan)
141
+ }
142
+
143
+ get rowspan() {
144
+ return this.getState('rowspan')
145
+ }
146
+
147
+ set colspan(colspan) {
148
+ this.set('colspan', colspan)
149
+ }
150
+
151
+ get colspan() {
152
+ return this.getState('colspan')
153
+ }
154
+
155
+ get border() {
156
+ return this.state.border || EMPTY_BORDER
157
+ }
158
+
159
+ get isEmpty() {
160
+ return this.getState('isEmpty')
161
+ }
162
+
163
+ _drawBorder(context: CanvasRenderingContext2D, x: number, y: number, to_x: number, to_y: number, style: any) {
164
+ if (style && style.strokeStyle && style.lineWidth && style.lineDash) {
165
+ context.beginPath()
166
+ context.moveTo(x, y)
167
+ context.lineTo(to_x, to_y)
168
+
169
+ Component.drawStroke(context, style)
170
+ }
171
+ }
172
+
173
+ render(context: CanvasRenderingContext2D) {
174
+ const { left, top, width, height } = this.bounds
175
+ const { isEmpty } = this.state
176
+
177
+ const border = this.border
178
+
179
+ if (!isEmpty) {
180
+ this._draw_rack_cell(context)
181
+ }
182
+
183
+ // Cell 채우기.
184
+ context.beginPath()
185
+ context.lineWidth = 0
186
+ context.rect(left, top, width, height)
187
+
188
+ // Border 그리기
189
+ const parent = this.parent as any
190
+ const idx = parent.components.indexOf(this)
191
+ const columns = parent.columns || 1
192
+ const rows = parent.rows || 1
193
+
194
+ this._drawBorder(context, left, top, left + width, top, border.top)
195
+ this._drawBorder(context, left, top + height, left, top, border.left)
196
+ if (isRightMost(idx, rows, columns))
197
+ this._drawBorder(context, left + width, top, left + width, top + height, border.right)
198
+ if (isBottomMost(idx, rows, columns))
199
+ this._drawBorder(context, left + width, top + height, left, top + height, border.bottom)
200
+ }
201
+
202
+ _draw_rack_cell(context: CanvasRenderingContext2D) {
203
+ const { left, top, width, height } = this.bounds
204
+
205
+ context.save()
206
+ context.fillStyle = EMPTY_CELL_FILL_STYLE
207
+ context.fillRect(left, top, width, height)
208
+
209
+ context.beginPath()
210
+ context.lineWidth = EMPTY_CELL_LINE_WIDTH
211
+ context.strokeStyle = EMPTY_CELL_STROKE_STYLE
212
+
213
+ context.moveTo(left, top)
214
+ context.lineTo(left + width, top + height)
215
+ context.moveTo(left + width, top)
216
+ context.lineTo(left, top + height)
217
+
218
+ context.stroke()
219
+ context.closePath()
220
+ context.restore()
221
+ }
222
+
223
+ get decotag() {
224
+ const rackTable = this.parent
225
+ let { locPattern, zone = '' } = rackTable.model
226
+
227
+ locPattern = locPattern.substring(0, locPattern.indexOf('{u}') + 3)
228
+
229
+ let locationString = ''
230
+ if (this.getState('section') && this.getState('unit'))
231
+ locationString = locPattern
232
+ .replace('{z}', zone)
233
+ .replace('{s}', this.getState('section'))
234
+ .replace('{u}', this.getState('unit'))
235
+
236
+ return locationString || ''
237
+ }
238
+
239
+ get index() {
240
+ const rackTable = this.parent as any
241
+ const index = rackTable.components.indexOf(this)
242
+
243
+ const rowIndex = Math.floor(index / rackTable.columns)
244
+ const columnIndex = index % rackTable.columns
245
+
246
+ return {
247
+ row: rowIndex,
248
+ column: columnIndex
249
+ }
250
+ }
251
+
252
+ get rowIndex() {
253
+ return this.index.row
254
+ }
255
+
256
+ get columnIndex() {
257
+ return this.index.column
258
+ }
259
+
260
+ get leftCell() {
261
+ const rackTable = this.parent as any
262
+
263
+ const rowIndex = this.rowIndex
264
+ const columnIndex = this.columnIndex
265
+
266
+ if (columnIndex === 0) return null
267
+
268
+ const leftCell = rackTable.components[rowIndex * rackTable.columns + columnIndex - 1]
269
+
270
+ return leftCell
271
+ }
272
+
273
+ get rightCell() {
274
+ const rackTable = this.parent as any
275
+
276
+ const rowIndex = this.rowIndex
277
+ const columnIndex = this.columnIndex
278
+
279
+ if (columnIndex === rackTable.columns) return null
280
+
281
+ const rightCell = rackTable.components[rowIndex * rackTable.columns + columnIndex + 1]
282
+
283
+ return rightCell
284
+ }
285
+
286
+ get aboveCell() {
287
+ const rackTable = this.parent as any
288
+
289
+ const rowIndex = this.rowIndex
290
+ const columnIndex = this.columnIndex
291
+
292
+ if (rowIndex === 0) return null
293
+
294
+ const aboveCell = rackTable.components[(rowIndex - 1) * rackTable.columns + columnIndex]
295
+
296
+ return aboveCell
297
+ }
298
+
299
+ get belowCell() {
300
+ const rackTable = this.parent as any
301
+
302
+ const rowIndex = this.rowIndex
303
+ const columnIndex = this.columnIndex
304
+
305
+ if (rowIndex === rackTable.rows) return null
306
+
307
+ const belowCell = rackTable.components[(rowIndex + 1) * rackTable.columns + columnIndex]
308
+
309
+ return belowCell
310
+ }
311
+
312
+ get rowCells() {
313
+ const rackTable = this.parent as any
314
+ return rackTable.getCellsByRow(this.rowIndex)
315
+ }
316
+
317
+ get columnCells() {
318
+ const rackTable = this.parent as any
319
+ return rackTable.getCellsByColumn(this.columnIndex)
320
+ }
321
+
322
+ get aboveRowCells() {
323
+ let aboveCell = this.aboveCell
324
+ while (1) {
325
+ const aboveRowCells = aboveCell.notEmptyRowCells
326
+
327
+ if (aboveRowCells.length > 0) return aboveRowCells
328
+
329
+ aboveCell = aboveCell.aboveCell
330
+ }
331
+ }
332
+
333
+ get lastUnit() {
334
+ const rowCells = this.aboveRowCells
335
+
336
+ for (let i = rowCells.length - 1; i > 0; i--) {
337
+ const cell = rowCells[i]
338
+
339
+ const unit = cell.getState('unit')
340
+
341
+ if (unit) return Number(unit)
342
+ }
343
+
344
+ return 0
345
+ }
346
+
347
+ get firstUnit() {
348
+ const rowCells = this.aboveRowCells
349
+
350
+ for (let i = 0; i < rowCells.length; i++) {
351
+ const cell = rowCells[i]
352
+
353
+ const unit = cell.getState('unit')
354
+
355
+ if (unit) return Number(unit)
356
+ }
357
+
358
+ return 0
359
+ }
360
+
361
+ get notEmptyRowCells() {
362
+ return this.rowCells.filter((c: Component) => {
363
+ return !c.getState('isEmpty')
364
+ })
365
+ }
366
+
367
+ get emptyRowCells() {
368
+ return this.rowCells.filter((c: Component) => {
369
+ return c.getState('isEmpty')
370
+ })
371
+ }
372
+
373
+ get isAisle() {
374
+ return this.notEmptyRowCells.length === 0
375
+ }
376
+
377
+ onchange(after: Properties, before: Properties) {
378
+ if (hasAnyProperty(after, 'isEmpty')) {
379
+ // FIXME
380
+ delete this.model.unit
381
+ delete this.model.section
382
+ }
383
+ }
384
+
385
+ onmouseenter() {
386
+ this.trigger('deco')
387
+ }
388
+
389
+ onmouseleave() {
390
+ this.trigger('decoreset')
391
+ }
392
+
393
+ contains(x: number, y: number) {
394
+ const contains = super.contains(x, y)
395
+ if (!contains) {
396
+ this._focused = false
397
+ this.invalidate()
398
+ }
399
+
400
+ return contains
401
+ }
402
+ }
403
+
404
+ ;['border'].forEach(getter => Component.memoize(RackGridCell.prototype, getter, false))
@@ -0,0 +1,77 @@
1
+ /*
2
+ * Copyright © HatioLab Inc. All rights reserved.
3
+ *
4
+ * RackTable pure helpers — separated for unit-testability (no scene-base /
5
+ * things-scene runtime deps).
6
+ */
7
+
8
+ /**
9
+ * Parse shelf-labels string into an array of length `levels`.
10
+ *
11
+ * Format: comma-separated. Empty positions fall back to `String(level + 1)`.
12
+ * ',,,04' over 4 levels → ['1', '2', '3', '04']
13
+ * '1,2,3' over 4 levels → ['1', '2', '3', '4']
14
+ */
15
+ export function parseShelfLabels(input: string | undefined, levels: number): string[] {
16
+ const out: string[] = []
17
+ const parts = (input || '').split(',')
18
+ for (let i = 0; i < levels; i++) {
19
+ const p = parts[i]
20
+ out[i] = p && p.trim().length > 0 ? p.trim() : String(i + 1)
21
+ }
22
+ return out
23
+ }
24
+
25
+ /**
26
+ * Apply a location pattern with the four standard placeholders.
27
+ *
28
+ * {z} → zone
29
+ * {s} → section (zero-padded outside)
30
+ * {u} → unit (zero-padded outside)
31
+ * {sh} → shelf
32
+ *
33
+ * Caller is responsible for the zero-padding of section/unit (the digit
34
+ * widths are state-driven, not part of the pattern itself).
35
+ */
36
+ export function formatLocationId(
37
+ zone: string,
38
+ section: string,
39
+ unit: string,
40
+ shelf: string,
41
+ pattern: string
42
+ ): string {
43
+ return pattern
44
+ .replace(/\{z\}/g, zone)
45
+ .replace(/\{s\}/g, section)
46
+ .replace(/\{u\}/g, unit)
47
+ .replace(/\{sh\}/g, shelf)
48
+ }
49
+
50
+ /**
51
+ * Distribute `total` over `n` slots, honoring per-slot declared sizes when
52
+ * present. Unspecified slots share the remaining space evenly.
53
+ *
54
+ * distribute(1000, 4, [200, undefined, 300, undefined]) → [200, 250, 300, 250]
55
+ * distribute(1000, 4, []) → [250, 250, 250, 250]
56
+ */
57
+ export function distribute(total: number, n: number, declared: (number | undefined)[] = []): number[] {
58
+ const out: number[] = []
59
+ let assigned = 0
60
+ let auto = 0
61
+ for (let i = 0; i < n; i++) {
62
+ const v = declared[i]
63
+ if (typeof v === 'number' && Number.isFinite(v)) {
64
+ out[i] = v
65
+ assigned += v
66
+ } else {
67
+ out[i] = -1
68
+ auto++
69
+ }
70
+ }
71
+ const remaining = Math.max(0, total - assigned)
72
+ const autoSize = auto > 0 ? remaining / auto : 0
73
+ for (let i = 0; i < n; i++) {
74
+ if (out[i] === -1) out[i] = autoSize
75
+ }
76
+ return out
77
+ }