@operato/scene-storage 10.0.0-beta.33 → 10.0.0-beta.35
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.
- package/CHANGELOG.md +23 -0
- package/dist/crane-3d.d.ts +14 -0
- package/dist/crane-3d.js +238 -0
- package/dist/crane-3d.js.map +1 -0
- package/dist/crane.d.ts +163 -0
- package/dist/crane.js +459 -0
- package/dist/crane.js.map +1 -0
- package/dist/index.d.ts +13 -6
- package/dist/index.js +9 -4
- package/dist/index.js.map +1 -1
- package/dist/mobile-storage-rack.d.ts +17 -0
- package/dist/mobile-storage-rack.js +55 -0
- package/dist/mobile-storage-rack.js.map +1 -0
- package/dist/rack-column.d.ts +35 -0
- package/dist/rack-column.js +258 -0
- package/dist/rack-column.js.map +1 -0
- package/dist/rack-grid-3d.d.ts +13 -0
- package/dist/rack-grid-3d.js +94 -0
- package/dist/rack-grid-3d.js.map +1 -0
- package/dist/rack-grid-cell.d.ts +341 -0
- package/dist/rack-grid-cell.js +321 -0
- package/dist/rack-grid-cell.js.map +1 -0
- package/dist/rack-grid-helpers.d.ts +28 -0
- package/dist/rack-grid-helpers.js +71 -0
- package/dist/rack-grid-helpers.js.map +1 -0
- package/dist/rack-grid-location.d.ts +37 -0
- package/dist/rack-grid-location.js +227 -0
- package/dist/rack-grid-location.js.map +1 -0
- package/dist/rack-grid.d.ts +80 -0
- package/dist/rack-grid.js +829 -0
- package/dist/rack-grid.js.map +1 -0
- package/dist/stock.d.ts +78 -0
- package/dist/stock.js +333 -0
- package/dist/stock.js.map +1 -0
- package/dist/{rack-cell-3d.d.ts → storage-cell-3d.d.ts} +1 -1
- package/dist/{rack-cell-3d.js → storage-cell-3d.js} +3 -3
- package/dist/storage-cell-3d.js.map +1 -0
- package/dist/{rack-cell.d.ts → storage-cell.d.ts} +12 -6
- package/dist/{rack-cell.js → storage-cell.js} +9 -9
- package/dist/storage-cell.js.map +1 -0
- package/dist/{asrs-rack-3d.d.ts → storage-rack-3d.d.ts} +1 -1
- package/dist/{asrs-rack-3d.js → storage-rack-3d.js} +4 -4
- package/dist/storage-rack-3d.js.map +1 -0
- package/dist/{asrs-rack.d.ts → storage-rack.d.ts} +22 -16
- package/dist/{asrs-rack.js → storage-rack.js} +32 -26
- package/dist/storage-rack.js.map +1 -0
- package/dist/templates/index.d.ts +60 -0
- package/dist/templates/index.js +59 -17
- package/dist/templates/index.js.map +1 -1
- package/package.json +2 -2
- package/src/crane-3d.ts +273 -0
- package/src/crane.ts +555 -0
- package/src/index.ts +13 -6
- package/src/mobile-storage-rack.ts +56 -0
- package/src/rack-column.ts +340 -0
- package/src/rack-grid-3d.ts +128 -0
- package/src/rack-grid-cell.ts +404 -0
- package/src/rack-grid-helpers.ts +77 -0
- package/src/rack-grid-location.ts +286 -0
- package/src/rack-grid.ts +994 -0
- package/src/stock.ts +426 -0
- package/src/{rack-cell-3d.ts → storage-cell-3d.ts} +2 -2
- package/src/{rack-cell.ts → storage-cell.ts} +19 -13
- package/src/{asrs-rack-3d.ts → storage-rack-3d.ts} +3 -3
- package/src/{asrs-rack.ts → storage-rack.ts} +31 -25
- package/src/templates/index.ts +59 -17
- package/test/test-rack-grid-crane.ts +212 -0
- package/test/test-rack-grid.ts +77 -0
- package/test/{test-asrs-crane.ts → test-storage-rack-crane.ts} +8 -8
- package/translations/en.json +55 -7
- package/translations/ja.json +52 -4
- package/translations/ko.json +52 -4
- package/translations/ms.json +55 -7
- package/translations/zh.json +52 -4
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/asrs-crane-3d.d.ts +0 -17
- package/dist/asrs-crane-3d.js +0 -181
- package/dist/asrs-crane-3d.js.map +0 -1
- package/dist/asrs-crane.d.ts +0 -98
- package/dist/asrs-crane.js +0 -216
- package/dist/asrs-crane.js.map +0 -1
- package/dist/asrs-rack-3d.js.map +0 -1
- package/dist/asrs-rack.js.map +0 -1
- package/dist/rack-cell-3d.js.map +0 -1
- package/dist/rack-cell.js.map +0 -1
- package/src/asrs-crane-3d.ts +0 -211
- package/src/asrs-crane.ts +0 -275
- /package/icons/{asrs-crane.png → crane.png} +0 -0
- /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
|
+
}
|