@fmsim/machine 2.0.0-beta.98 → 2.0.0
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/dist/carrier-manager.js +9 -1
- package/dist/carrier-manager.js.map +1 -1
- package/dist/carrier.js +69 -52
- package/dist/carrier.js.map +1 -1
- package/dist/crane.js +15 -6
- package/dist/crane.js.map +1 -1
- package/dist/factories/agv-3d.js +9 -27
- package/dist/factories/agv-3d.js.map +1 -1
- package/dist/factories/conveyor-3d.js +11 -19
- package/dist/factories/conveyor-3d.js.map +1 -1
- package/dist/factories/crane-3d.js +8 -17
- package/dist/factories/crane-3d.js.map +1 -1
- package/dist/factories/crane-rail-3d.js +8 -12
- package/dist/factories/crane-rail-3d.js.map +1 -1
- package/dist/factories/machine-3d.js +100 -63
- package/dist/factories/machine-3d.js.map +1 -1
- package/dist/factories/oht-3d.js +9 -27
- package/dist/factories/oht-3d.js.map +1 -1
- package/dist/factories/shuttle-3d.js +11 -27
- package/dist/factories/shuttle-3d.js.map +1 -1
- package/dist/features/capacity-gauge-mixin.js +520 -0
- package/dist/features/capacity-gauge-mixin.js.map +1 -0
- package/dist/features/mcs-status-default.js +13 -0
- package/dist/features/mcs-status-default.js.map +1 -1
- package/dist/features/parent-object-mixin.js +5 -1
- package/dist/features/parent-object-mixin.js.map +1 -1
- package/dist/mcs-machine.js +4 -23
- package/dist/mcs-machine.js.map +1 -1
- package/dist/mcs-unit.js +2 -5
- package/dist/mcs-unit.js.map +1 -1
- package/dist/mcs-vehicle.js +3 -1
- package/dist/mcs-vehicle.js.map +1 -1
- package/dist/scene/root-container-override.js +5 -2
- package/dist/scene/root-container-override.js.map +1 -1
- package/dist/stocker-capacity-bar.js +11 -372
- package/dist/stocker-capacity-bar.js.map +1 -1
- package/dist/stocker.js +29 -1
- package/dist/stocker.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/utils/crane-position.js +23 -14
- package/dist/utils/crane-position.js.map +1 -1
- package/dist/utils/stocker-gauge.js +48 -10
- package/dist/utils/stocker-gauge.js.map +1 -1
- package/dist/zone-capacity-bar.js +13 -213
- package/dist/zone-capacity-bar.js.map +1 -1
- package/package.json +4 -4
- package/src/carrier-manager.ts +9 -1
- package/src/carrier.ts +73 -52
- package/src/crane.ts +14 -5
- package/src/factories/agv-3d.ts +10 -26
- package/src/factories/conveyor-3d.ts +12 -19
- package/src/factories/crane-3d.ts +9 -17
- package/src/factories/crane-rail-3d.ts +9 -13
- package/src/factories/machine-3d.ts +125 -66
- package/src/factories/oht-3d.ts +10 -26
- package/src/factories/shuttle-3d.ts +12 -26
- package/src/features/capacity-gauge-mixin.ts +642 -0
- package/src/features/mcs-status-default.ts +13 -0
- package/src/features/parent-object-mixin.ts +5 -1
- package/src/mcs-machine.ts +4 -23
- package/src/mcs-unit.ts +2 -5
- package/src/mcs-vehicle.ts +3 -1
- package/src/scene/root-container-override.ts +5 -2
- package/src/stocker-capacity-bar.ts +15 -476
- package/src/stocker.ts +29 -1
- package/src/utils/crane-position.ts +46 -14
- package/src/utils/stocker-gauge.ts +63 -10
- package/src/zone-capacity-bar.ts +17 -282
- package/translations/en.json +4 -1
- package/translations/ja.json +4 -1
- package/translations/ko.json +4 -1
- package/translations/ms.json +4 -1
- package/translations/zh.json +4 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shuttle-3d.js","sourceRoot":"","sources":["../../src/factories/shuttle-3d.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Shuttle 3D Factory\n *\n * 스토커 내부에서 캐리어를 선반↔포트 간 이송하는 포크 캐리지.\n * 수평 레일 위를 이동하며, 포크를 밀어넣어 캐리어를 적재/하역.\n *\n * 구조 (바닥 원점, y=0~depth):\n * - 하단 베이스 플레이트 (납작, 넓음, 레일 위 주행부)\n * - 중앙 마스트 (좌우 수직 기둥, statusColor 적용)\n * - 상단 포크 (2개의 평행한 가로 암, 캐리어 적재면)\n */\n\nimport * as THREE from 'three'\nimport * as BufferGeometryUtils from 'three/examples/jsm/utils/BufferGeometryUtils.js'\nimport { RealObjectGroup, registerRealObjectFactory, disposeObject3D } from '@hatiolab/things-scene'\nimport type { Component } from '@hatiolab/things-scene'\nimport { TRANSPORT_SURFACE_HEIGHT } from './machine-3d.js'\n\nconst BASE_COLOR = 0x333333\nconst FORK_COLOR = 0x777777\nconst DEFAULT_BODY_COLOR = 0x6688cc\n\nexport class Shuttle3D extends RealObjectGroup {\n private _ownMeshes: THREE.Mesh[] = []\n get effectiveDepth(): number {\n const { depth } = this.component.state\n return depth || TRANSPORT_SURFACE_HEIGHT\n }\n\n private resolveColor(): number {\n const comp = this.component as any\n\n try {\n const sc = comp.statusColor\n if (typeof sc === 'string' && sc && sc.toUpperCase() !== '#F0F0F0') return new THREE.Color(sc).getHex()\n } catch {\n /* statusColor 미구현 시 무시 */\n }\n\n const { fillStyle } = this.component.state\n if (fillStyle && typeof fillStyle === 'string') {\n try {\n return new THREE.Color(fillStyle).getHex()\n } catch {\n /* 파싱 실패 시 무시 */\n }\n }\n\n return DEFAULT_BODY_COLOR\n }\n\n build() {\n super.build()\n\n const { width = 10, height = 10 } = this.component.state\n const w = Math.abs(width)\n const h = Math.abs(height)\n const d = this.effectiveDepth\n if (!w || !h) return\n\n // 높이 배분: 베이스 15%, 마스트 55%, 포크 30%\n const baseH = d * 0.15\n const mastH = d * 0.55\n const forkH = d * 0.30\n\n const mastThickness = Math.min(w, h) * 0.15\n\n // ── 1. 하단 베이스 플레이트 (납작하고 넓은 주행부) ──\n const basePad = Math.min(w, h) * 0.05\n const baseGeo = new THREE.BoxGeometry(w + basePad * 2, baseH, h + basePad * 2)\n const baseMat = new THREE.MeshStandardMaterial({\n color: BASE_COLOR,\n roughness: 0.7,\n metalness: 0.3,\n })\n const baseMesh = new THREE.Mesh(baseGeo, baseMat)\n baseMesh.position.y = baseH / 2\n baseMesh.castShadow = true\n baseMesh.receiveShadow = true\n\n // ── 2. 좌우 마스트 기둥 (statusColor 적용) ──\n const mastBaseY = baseH\n const bodyColor = this.resolveColor()\n const mastMat = new THREE.MeshStandardMaterial({\n color: bodyColor,\n roughness: 0.4,\n metalness: 0.5,\n })\n\n const mastGeometries: THREE.BufferGeometry[] = []\n\n // 좌우 수직 기둥\n for (const xSign of [-1, 1]) {\n const pillar = new THREE.BoxGeometry(mastThickness, mastH, mastThickness)\n pillar.translate(xSign * (w / 2 - mastThickness / 2), mastBaseY + mastH / 2, 0)\n mastGeometries.push(pillar)\n }\n\n // 중간 가로 연결 바 (구조 강성)\n const crossBarW = w - mastThickness\n const crossBarH = mastThickness * 0.5\n const crossBar = new THREE.BoxGeometry(crossBarW, crossBarH, mastThickness)\n crossBar.translate(0, mastBaseY + mastH * 0.4, 0)\n mastGeometries.push(crossBar)\n\n const mastMesh = new THREE.Mesh(BufferGeometryUtils.mergeGeometries(mastGeometries), mastMat)\n mastMesh.castShadow = true\n\n // ── 3. 상단 포크 (2개의 평행 암 + 백 플레이트) ──\n const forkBaseY = mastBaseY + mastH\n const forkMat = new THREE.MeshStandardMaterial({\n color: FORK_COLOR,\n roughness: 0.4,\n metalness: 0.6,\n })\n\n const forkGeometries: THREE.BufferGeometry[] = []\n\n // 백 플레이트 (마스트 상단을 잇는 가로판)\n const backPlateH = forkH * 0.4\n const backPlate = new THREE.BoxGeometry(w, backPlateH, mastThickness * 0.8)\n backPlate.translate(0, forkBaseY + backPlateH / 2, -h * 0.35)\n forkGeometries.push(backPlate)\n\n // 2개의 포크 암 (앞으로 뻗은 평행 막대)\n const forkArmW = mastThickness * 0.7\n const forkArmH = forkH * 0.25\n const forkArmD = h * 0.85\n const forkSpacing = w * 0.55\n\n for (const xSign of [-1, 1]) {\n const arm = new THREE.BoxGeometry(forkArmW, forkArmH, forkArmD)\n arm.translate(xSign * (forkSpacing / 2), forkBaseY + forkArmH / 2, forkArmD / 2 - h * 0.35)\n forkGeometries.push(arm)\n }\n\n const forkMesh = new THREE.Mesh(BufferGeometryUtils.mergeGeometries(forkGeometries), forkMat)\n forkMesh.castShadow = true\n\n this._ownMeshes = [baseMesh, mastMesh, forkMesh]\n for (const m of this._ownMeshes) this.components3D.add(m)\n }\n\n updateDimension() {}\n\n update() {\n for (const m of this._ownMeshes) {\n this.components3D.remove(m)\n disposeObject3D(m)\n }\n this._ownMeshes = []\n this.build()\n this.updateTransform()\n this.updateAlpha()\n this.updateHidden()\n }\n\n updateAlpha() {\n const { alpha = 1 } = this.component.state\n for (const m of this._ownMeshes) {\n const materials = Array.isArray(m.material) ? m.material : [m.material]\n for (const mat of materials) {\n ;(mat as THREE.MeshStandardMaterial).opacity = alpha\n ;(mat as THREE.MeshStandardMaterial).transparent = alpha < 1\n }\n }\n }\n\n onchange(after: Record<string, unknown>, before: Record<string, unknown>) {\n if ('width' in after || 'height' in after || 'depth' in after ||\n 'fillStyle' in after || 'strokeStyle' in after || 'id' in after) {\n this.update()\n return\n }\n super.onchange(after, before)\n }\n}\n\nregisterRealObjectFactory('Shuttle', (component: Component) => new Shuttle3D(component))\n"]}
|
|
1
|
+
{"version":3,"file":"shuttle-3d.js","sourceRoot":"","sources":["../../src/factories/shuttle-3d.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Shuttle 3D Factory\n *\n * 스토커 내부에서 캐리어를 선반↔포트 간 이송하는 포크 캐리지.\n * 수평 레일 위를 이동하며, 포크를 밀어넣어 캐리어를 적재/하역.\n *\n * 구조 (바닥 원점, y=0~depth):\n * - 하단 베이스 플레이트 (납작, 넓음, 레일 위 주행부)\n * - 중앙 마스트 (좌우 수직 기둥, statusColor 적용)\n * - 상단 포크 (2개의 평행한 가로 암, 캐리어 적재면)\n */\n\nimport * as THREE from 'three'\nimport * as BufferGeometryUtils from 'three/examples/jsm/utils/BufferGeometryUtils.js'\nimport { RealObjectGroup, registerRealObjectFactory, disposeObject3D, MaterialBank } from '@hatiolab/things-scene'\nimport type { Component } from '@hatiolab/things-scene'\nimport { TRANSPORT_SURFACE_HEIGHT } from './machine-3d.js'\n\nconst BASE_COLOR = 0x333333\nconst FORK_COLOR = 0x777777\nconst DEFAULT_BODY_COLOR = 0x6688cc\n\nexport class Shuttle3D extends RealObjectGroup {\n private _ownMeshes: THREE.Mesh[] = []\n get effectiveDepth(): number {\n const { depth } = this.component.state\n return depth || TRANSPORT_SURFACE_HEIGHT\n }\n\n private resolveColor(): number {\n const comp = this.component as any\n\n try {\n const sc = comp.statusColor\n if (typeof sc === 'string' && sc && sc.toUpperCase() !== '#F0F0F0') return new THREE.Color(sc).getHex()\n } catch {\n /* statusColor 미구현 시 무시 */\n }\n\n const { fillStyle } = this.component.state\n if (fillStyle && typeof fillStyle === 'string') {\n try {\n return new THREE.Color(fillStyle).getHex()\n } catch {\n /* 파싱 실패 시 무시 */\n }\n }\n\n return DEFAULT_BODY_COLOR\n }\n\n build() {\n super.build()\n\n const { width = 10, height = 10 } = this.component.state\n const w = Math.abs(width)\n const h = Math.abs(height)\n const d = this.effectiveDepth\n if (!w || !h) return\n\n // 높이 배분: 베이스 15%, 마스트 55%, 포크 30%\n const baseH = d * 0.15\n const mastH = d * 0.55\n const forkH = d * 0.30\n\n const mastThickness = Math.min(w, h) * 0.15\n\n // ── 1. 하단 베이스 플레이트 (납작하고 넓은 주행부) ──\n const basePad = Math.min(w, h) * 0.05\n const baseGeo = new THREE.BoxGeometry(w + basePad * 2, baseH, h + basePad * 2)\n // MaterialBank 공유 — 정적/상태색 dedup + faint 안전(코어가 공유 base 변형 스왑). dispose 는\n // disposeObject3D 가 __bankOwned 를 건너뛰어 안전.\n const baseMat = MaterialBank.material('shuttle|base', { roughness: 0.7, metalness: 0.3 }, BASE_COLOR)\n const baseMesh = new THREE.Mesh(baseGeo, baseMat)\n baseMesh.position.y = baseH / 2\n baseMesh.castShadow = true\n baseMesh.receiveShadow = true\n\n // ── 2. 좌우 마스트 기둥 (statusColor 적용) ──\n const mastBaseY = baseH\n const bodyColor = this.resolveColor()\n const mastMat = MaterialBank.material('shuttle|mast', { roughness: 0.4, metalness: 0.5 }, bodyColor)\n\n const mastGeometries: THREE.BufferGeometry[] = []\n\n // 좌우 수직 기둥\n for (const xSign of [-1, 1]) {\n const pillar = new THREE.BoxGeometry(mastThickness, mastH, mastThickness)\n pillar.translate(xSign * (w / 2 - mastThickness / 2), mastBaseY + mastH / 2, 0)\n mastGeometries.push(pillar)\n }\n\n // 중간 가로 연결 바 (구조 강성)\n const crossBarW = w - mastThickness\n const crossBarH = mastThickness * 0.5\n const crossBar = new THREE.BoxGeometry(crossBarW, crossBarH, mastThickness)\n crossBar.translate(0, mastBaseY + mastH * 0.4, 0)\n mastGeometries.push(crossBar)\n\n const mastMesh = new THREE.Mesh(BufferGeometryUtils.mergeGeometries(mastGeometries), mastMat)\n mastMesh.castShadow = true\n\n // ── 3. 상단 포크 (2개의 평행 암 + 백 플레이트) ──\n const forkBaseY = mastBaseY + mastH\n const forkMat = MaterialBank.material('shuttle|fork', { roughness: 0.4, metalness: 0.6 }, FORK_COLOR)\n\n const forkGeometries: THREE.BufferGeometry[] = []\n\n // 백 플레이트 (마스트 상단을 잇는 가로판)\n const backPlateH = forkH * 0.4\n const backPlate = new THREE.BoxGeometry(w, backPlateH, mastThickness * 0.8)\n backPlate.translate(0, forkBaseY + backPlateH / 2, -h * 0.35)\n forkGeometries.push(backPlate)\n\n // 2개의 포크 암 (앞으로 뻗은 평행 막대)\n const forkArmW = mastThickness * 0.7\n const forkArmH = forkH * 0.25\n const forkArmD = h * 0.85\n const forkSpacing = w * 0.55\n\n for (const xSign of [-1, 1]) {\n const arm = new THREE.BoxGeometry(forkArmW, forkArmH, forkArmD)\n arm.translate(xSign * (forkSpacing / 2), forkBaseY + forkArmH / 2, forkArmD / 2 - h * 0.35)\n forkGeometries.push(arm)\n }\n\n const forkMesh = new THREE.Mesh(BufferGeometryUtils.mergeGeometries(forkGeometries), forkMat)\n forkMesh.castShadow = true\n\n this._ownMeshes = [baseMesh, mastMesh, forkMesh]\n for (const m of this._ownMeshes) this.components3D.add(m)\n\n // mesh 는 super.build() 이후 생성 → 코어 faint 를 명시 적용(편집 흐림이 mesh 에 닿게).\n this.updateAlpha()\n }\n\n updateDimension() {}\n\n update() {\n for (const m of this._ownMeshes) {\n this.components3D.remove(m)\n disposeObject3D(m)\n }\n this._ownMeshes = []\n this.build()\n this.updateTransform()\n this.updateAlpha()\n this.updateHidden()\n }\n\n // updateAlpha override 제거 — 코어 RealObject.updateAlpha(=applyMaterialOpacity)로 수렴.\n // 코어가 canonical(=bank base, opacity 1) 위에 state.alpha × faint 를 일괄. view 모드는\n // 1×state.alpha = 기존 in-place 결과와 동일 → 불변, 편집 모드만 faint 추가.\n\n onchange(after: Record<string, unknown>, before: Record<string, unknown>) {\n if ('width' in after || 'height' in after || 'depth' in after ||\n 'fillStyle' in after || 'strokeStyle' in after || 'id' in after) {\n this.update()\n return\n }\n super.onchange(after, before)\n }\n}\n\nregisterRealObjectFactory('Shuttle', (component: Component) => new Shuttle3D(component))\n"]}
|
|
@@ -0,0 +1,520 @@
|
|
|
1
|
+
import { themesColorRange } from '@fmsim/api';
|
|
2
|
+
import { LEGEND_CAPACITY, resolveCarrierLegend } from './mcs-status-default';
|
|
3
|
+
import { safeRound } from '../utils/safe-round';
|
|
4
|
+
import { getValueOnRanges } from '../utils/get-value-on-ranges';
|
|
5
|
+
import { carrierSegments, abnormalSegments, carrierColorsFromLegend, pickSegmentAt } from '../utils/stocker-gauge';
|
|
6
|
+
/**
|
|
7
|
+
* 용량 게이지 (통합 로직).
|
|
8
|
+
*
|
|
9
|
+
* 하나의 컴포넌트가 mode 에 따라 3가지 게이지를 표현한다:
|
|
10
|
+
* - 'capacity' : 용량 사용률(usage%) 채움 게이지 + high/low watermark (기본)
|
|
11
|
+
* - 'carrier' : 캐리어 상태비율 세그먼트 (Full / Empty / EmptyEmpty)
|
|
12
|
+
* - 'abnormal' : 캐리어 정상/비정상 비율 세그먼트 (Normal / Abnormal)
|
|
13
|
+
*
|
|
14
|
+
* StockerCapacityBar(스토커 지붕 게이지)와 ZoneCapacityBar(존 게이지)가 이 로직을 공유한다.
|
|
15
|
+
* 두 컴포넌트는 base(스토커=Shape, 존=Container)·정체성(identifiable)·legend 폴백만 다르고,
|
|
16
|
+
* 게이지를 그리는 규칙은 완전히 동일하다 — 규칙을 이 믹스인 한 곳에 두어 drift 를 막는다.
|
|
17
|
+
*
|
|
18
|
+
* mode 소스: root.stockerGaugeMode (기본 'capacity'). RootContainer 의 런타임 전용
|
|
19
|
+
* transient 속성으로, 3D(machine-3d buildGauge)와도 동일 소스라 2D/3D 게이지가 항상 일치한다.
|
|
20
|
+
* (root-container-override.ts 정의. 모드가 바뀌면 _refreshStockerGauges 가 두 게이지를 갱신.)
|
|
21
|
+
*
|
|
22
|
+
* 데이터 필드(2D 기준, canonical):
|
|
23
|
+
* - capacity : CURRENTCAPACITY / MAXCAPACITY / HIGHWATERMARK / LOWWATERMARK / FULLUP
|
|
24
|
+
* - carrier : CARRIER_FULL / CARRIER_EMPTY / CARRIER_EMPTY_EMPTY
|
|
25
|
+
* - abnormal : CARRIER_NORMAL / CARRIER_ABNORMAL
|
|
26
|
+
*/
|
|
27
|
+
/** 게이지 NATURE 속성 조각 (스토커/존 공통). 각 컴포넌트 NATURE 에 spread 로 합친다. */
|
|
28
|
+
export const CapacityGaugeMixinProperties = [
|
|
29
|
+
{
|
|
30
|
+
type: 'select',
|
|
31
|
+
label: 'legend-name',
|
|
32
|
+
name: 'legendName',
|
|
33
|
+
property: {
|
|
34
|
+
options: themesColorRange
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
type: 'number',
|
|
39
|
+
name: 'currentUsage',
|
|
40
|
+
label: 'current-usage'
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
type: 'number',
|
|
44
|
+
name: 'highWatermark',
|
|
45
|
+
label: 'high-watermark'
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
type: 'number',
|
|
49
|
+
name: 'lowWatermark',
|
|
50
|
+
label: 'low-watermark'
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
type: 'number',
|
|
54
|
+
name: 'maxCapacity',
|
|
55
|
+
label: 'max-capacity'
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
type: 'boolean',
|
|
59
|
+
name: 'fullup',
|
|
60
|
+
label: 'fullup'
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
type: 'number',
|
|
64
|
+
name: 'carrierFull',
|
|
65
|
+
label: 'carrier-full'
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
type: 'number',
|
|
69
|
+
name: 'carrierEmpty',
|
|
70
|
+
label: 'carrier-empty'
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
type: 'number',
|
|
74
|
+
name: 'carrierEmptyEmpty',
|
|
75
|
+
label: 'carrier-empty-empty'
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
type: 'number',
|
|
79
|
+
name: 'carrierNormal',
|
|
80
|
+
label: 'carrier-normal'
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
type: 'number',
|
|
84
|
+
name: 'carrierAbnormal',
|
|
85
|
+
label: 'carrier-abnormal'
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
type: 'number',
|
|
89
|
+
label: 'round',
|
|
90
|
+
name: 'round',
|
|
91
|
+
property: {
|
|
92
|
+
min: 0
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
type: 'hidden',
|
|
97
|
+
name: 'usage',
|
|
98
|
+
label: 'usage'
|
|
99
|
+
}
|
|
100
|
+
];
|
|
101
|
+
const controlHandler = {
|
|
102
|
+
ondragmove: function (point, index, component) {
|
|
103
|
+
var { left, top, width, height } = component.model;
|
|
104
|
+
var transcoorded = component.transcoordP2S(point.x, point.y);
|
|
105
|
+
var round = ((transcoorded.x - left) / (width / 2)) * 100;
|
|
106
|
+
round = safeRound(round, width, height);
|
|
107
|
+
component.set({
|
|
108
|
+
round
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
export function CapacityGaugeMixin(Base) {
|
|
113
|
+
return class extends Base {
|
|
114
|
+
/** 게이지 모드: RootContainer 런타임 transient 속성. 3D(machine-3d) 와 동일 소스. */
|
|
115
|
+
get gaugeMode() {
|
|
116
|
+
var _a;
|
|
117
|
+
return ((_a = this.root) === null || _a === void 0 ? void 0 : _a.stockerGaugeMode) || 'capacity';
|
|
118
|
+
}
|
|
119
|
+
get controls() {
|
|
120
|
+
var { left, top, width, round, height } = this.state;
|
|
121
|
+
round = round == undefined ? 0 : safeRound(round, width, height);
|
|
122
|
+
return [
|
|
123
|
+
{
|
|
124
|
+
x: left + (width / 2) * (round / 100),
|
|
125
|
+
y: top,
|
|
126
|
+
handler: controlHandler
|
|
127
|
+
}
|
|
128
|
+
];
|
|
129
|
+
}
|
|
130
|
+
getTheme() {
|
|
131
|
+
var _a, _b;
|
|
132
|
+
const { legendName } = this.state;
|
|
133
|
+
if (legendName) {
|
|
134
|
+
return (_b = (_a = this.root) === null || _a === void 0 ? void 0 : _a.style) === null || _b === void 0 ? void 0 : _b[legendName];
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
get legend() {
|
|
138
|
+
return this.getTheme() || this.getLegendFallback();
|
|
139
|
+
}
|
|
140
|
+
/** legend 미지정 시 폴백 테마. 컴포넌트별로 override (스토커/존 각자 전역 테마). */
|
|
141
|
+
getLegendFallback() {
|
|
142
|
+
return LEGEND_CAPACITY;
|
|
143
|
+
}
|
|
144
|
+
get statusColor() {
|
|
145
|
+
const { fullup } = this.state;
|
|
146
|
+
return fullup ? 'red' : getValueOnRanges(this.usage, this.legend);
|
|
147
|
+
}
|
|
148
|
+
get text() {
|
|
149
|
+
// usage% 값은 capacity 모드에서만 표시. carrier/abnormal 은 세그먼트별 자체 라벨을 그리므로
|
|
150
|
+
// base 의 value 텍스트를 억제한다.
|
|
151
|
+
if (this.gaugeMode !== 'capacity') {
|
|
152
|
+
return '';
|
|
153
|
+
}
|
|
154
|
+
return this.usage + '%';
|
|
155
|
+
}
|
|
156
|
+
render(context) {
|
|
157
|
+
const mode = this.gaugeMode;
|
|
158
|
+
if (mode === 'carrier' || mode === 'abnormal') {
|
|
159
|
+
// carrier/abnormal 모드는 세그먼트 값이 전부 0(데이터 없음)일 때 빈 막대 대신 '0%' 를 표시한다.
|
|
160
|
+
this.renderSegmentGauge(context, this.segments(mode), true);
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
// capacity (기본)
|
|
164
|
+
const { width, height } = this.bounds;
|
|
165
|
+
if (width >= height) {
|
|
166
|
+
this.renderLandscape(context);
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
this.renderPortrait(context);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* carrier/abnormal mode 의 세그먼트 목록 (공유 유틸 = 3D 와 동일 계산).
|
|
174
|
+
* 배열 순서 = portrait 아래→위 / landscape 왼→오.
|
|
175
|
+
*/
|
|
176
|
+
segments(mode) {
|
|
177
|
+
if (mode === 'abnormal') {
|
|
178
|
+
const { carrierNormal = 0, carrierAbnormal = 0 } = this.state;
|
|
179
|
+
return abnormalSegments(carrierNormal, carrierAbnormal);
|
|
180
|
+
}
|
|
181
|
+
const { carrierFull = 0, carrierEmpty = 0, carrierEmptyEmpty = 0 } = this.state;
|
|
182
|
+
// 색상은 carrier 컴포넌트와 동일한 legend(테마)에서 취득 (carrierColorsFromLegend).
|
|
183
|
+
const colors = carrierColorsFromLegend(resolveCarrierLegend(this));
|
|
184
|
+
return carrierSegments(carrierFull, carrierEmpty, carrierEmptyEmpty, colors);
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* 클릭(또는 hover) 지점이 어떤 세그먼트에 속하는지 판정. carrier/abnormal 모드 전용 —
|
|
188
|
+
* capacity 모드·데이터 없음·좌표 변환 실패 시 null.
|
|
189
|
+
*
|
|
190
|
+
* renderSegmentGauge 와 동일한 배치 규칙으로 매핑한다: landscape(가로) 는 왼→오(누적 x),
|
|
191
|
+
* portrait(세로) 는 아래→위(바닥부터 누적). ratio 0 세그먼트는 화면에 없으므로 건너뛴다.
|
|
192
|
+
*/
|
|
193
|
+
segmentAtEvent(event) {
|
|
194
|
+
var _a;
|
|
195
|
+
const mode = this.gaugeMode;
|
|
196
|
+
if (mode !== 'carrier' && mode !== 'abnormal')
|
|
197
|
+
return null;
|
|
198
|
+
// 3D 게이지는 부모 STOCKER 의 real-object 가 그린다(이 컴포넌트가 아님) → 2D 세그먼트 판정 대상 아님.
|
|
199
|
+
if ((_a = this.root) === null || _a === void 0 ? void 0 : _a.is3dMode)
|
|
200
|
+
return null;
|
|
201
|
+
const ox = event === null || event === void 0 ? void 0 : event.offsetX;
|
|
202
|
+
const oy = event === null || event === void 0 ? void 0 : event.offsetY;
|
|
203
|
+
if (typeof ox !== 'number' || typeof oy !== 'number')
|
|
204
|
+
return null;
|
|
205
|
+
// 표면 픽셀(offsetX/offsetY) → 이 컴포넌트 로컬 프레임.
|
|
206
|
+
// 주의: transcoordP2S 는 '부모→로컬' 한 단계라 캔버스 픽셀로 단독 호출하면 안 된다.
|
|
207
|
+
// 엔진 capture() 와 동일하게 루트부터 전체 체인을 적용하는 helper 가 toLocal.
|
|
208
|
+
let local;
|
|
209
|
+
try {
|
|
210
|
+
local = this.toLocal(ox, oy);
|
|
211
|
+
}
|
|
212
|
+
catch (_b) {
|
|
213
|
+
return null;
|
|
214
|
+
}
|
|
215
|
+
if (!local)
|
|
216
|
+
return null;
|
|
217
|
+
const { left, top, width, height } = this.bounds;
|
|
218
|
+
if (!(width > 0) || !(height > 0))
|
|
219
|
+
return null;
|
|
220
|
+
// 컴포넌트 로컬프레임 좌표 → bounds 기준 within-box. 경계 반올림 방어로 clamp.
|
|
221
|
+
const lx = Math.min(Math.max(local.x - left, 0), width);
|
|
222
|
+
const ly = Math.min(Math.max(local.y - top, 0), height);
|
|
223
|
+
const landscape = width >= height;
|
|
224
|
+
const pos = landscape ? lx : height - ly; // portrait 는 바닥 기준 거리
|
|
225
|
+
const span = landscape ? width : height;
|
|
226
|
+
const picked = pickSegmentAt(this.segments(mode), pos, span);
|
|
227
|
+
if (!picked)
|
|
228
|
+
return null;
|
|
229
|
+
// label 은 key 로 도출 가능하고 로케일 의존이라 payload 에서 제외 — key(대문자 토큰)로 식별.
|
|
230
|
+
return { mode, key: picked.key, count: picked.count };
|
|
231
|
+
}
|
|
232
|
+
/** 세그먼트 게이지 렌더 (carrier/abnormal 공통). bounds 방향으로 landscape/portrait 자동. */
|
|
233
|
+
renderSegmentGauge(context, segments, zeroPercentWhenEmpty = false) {
|
|
234
|
+
const { left, top, width, height } = this.bounds;
|
|
235
|
+
const { lineWidth, strokeStyle, round, fillStyle = 'white' } = this.state;
|
|
236
|
+
const landscape = width >= height;
|
|
237
|
+
context.save();
|
|
238
|
+
context.translate(left, top);
|
|
239
|
+
// background
|
|
240
|
+
context.beginPath();
|
|
241
|
+
context.fillStyle = fillStyle;
|
|
242
|
+
context.roundRect(0, 0, width, height, round);
|
|
243
|
+
context.fill();
|
|
244
|
+
context.clip();
|
|
245
|
+
if (landscape) {
|
|
246
|
+
// 왼→오
|
|
247
|
+
let x = 0;
|
|
248
|
+
for (const seg of segments) {
|
|
249
|
+
const segWidth = width * seg.ratio;
|
|
250
|
+
if (segWidth <= 0)
|
|
251
|
+
continue;
|
|
252
|
+
context.beginPath();
|
|
253
|
+
context.fillStyle = seg.color;
|
|
254
|
+
context.rect(x, 0, segWidth, height);
|
|
255
|
+
context.fill();
|
|
256
|
+
this._drawSegmentLabel(context, seg, x, 0, segWidth, height);
|
|
257
|
+
x += segWidth;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
else {
|
|
261
|
+
// 아래→위 (배열 첫 세그먼트가 바닥)
|
|
262
|
+
let y = height;
|
|
263
|
+
for (const seg of segments) {
|
|
264
|
+
const segHeight = height * seg.ratio;
|
|
265
|
+
if (segHeight <= 0)
|
|
266
|
+
continue;
|
|
267
|
+
y -= segHeight;
|
|
268
|
+
context.beginPath();
|
|
269
|
+
context.fillStyle = seg.color;
|
|
270
|
+
context.rect(0, y, width, segHeight);
|
|
271
|
+
context.fill();
|
|
272
|
+
this._drawSegmentLabel(context, seg, 0, y, width, segHeight);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
// 데이터가 전혀 없어(모든 ratio 0) 세그먼트가 하나도 안 그려진 경우 —
|
|
276
|
+
// carrier/abnormal 모드는 빈 막대 대신 '0%' 를 막대 중앙에 표시한다.
|
|
277
|
+
if (zeroPercentWhenEmpty && segments.every(s => s.ratio <= 0)) {
|
|
278
|
+
this._drawSegmentLabel(context, { key: 'EMPTY', label: '', count: 0, ratio: 0, color: '' }, 0, 0, width, height);
|
|
279
|
+
}
|
|
280
|
+
// outline
|
|
281
|
+
context.beginPath();
|
|
282
|
+
context.setLineDash([]);
|
|
283
|
+
context.strokeStyle = strokeStyle;
|
|
284
|
+
context.lineWidth = lineWidth;
|
|
285
|
+
context.roundRect(0, 0, width, height, round);
|
|
286
|
+
context.stroke();
|
|
287
|
+
context.translate(-left, -top);
|
|
288
|
+
context.restore();
|
|
289
|
+
}
|
|
290
|
+
_drawSegmentLabel(context, seg, x, y, w, h) {
|
|
291
|
+
// 이름 라벨(Full/Empty/Abnormal…)은 그리지 않는다. 각 세그먼트 영역 중심에 숫자(%)만 표시.
|
|
292
|
+
// 컴포넌트의 폰트 설정(fontFamily/fontSize/bold/italic)을 그대로 적용 — this.font.
|
|
293
|
+
const fontSize = this.state.fontSize || 15;
|
|
294
|
+
context.font = this.font;
|
|
295
|
+
// 공간에 따라 단계적 축소: 넉넉하면 '값%', 좁으면 '%' 떼고 값만, 더 좁으면 생략.
|
|
296
|
+
const value = `${Math.round(seg.ratio * 100)}`;
|
|
297
|
+
const fits = (t) => fontSize <= h && context.measureText(t).width <= w;
|
|
298
|
+
let text = null;
|
|
299
|
+
if (fits(`${value}%`)) {
|
|
300
|
+
text = `${value}%`;
|
|
301
|
+
}
|
|
302
|
+
else if (fits(value)) {
|
|
303
|
+
text = value;
|
|
304
|
+
}
|
|
305
|
+
if (text === null) {
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
context.fillStyle = '#333';
|
|
309
|
+
context.textAlign = 'center';
|
|
310
|
+
context.textBaseline = 'middle';
|
|
311
|
+
context.fillText(text, x + w / 2, y + h / 2);
|
|
312
|
+
}
|
|
313
|
+
renderLandscape(context) {
|
|
314
|
+
const { left, top, width, height } = this.bounds;
|
|
315
|
+
const { highWatermark, lowWatermark, lineWidth, strokeStyle, round, fillStyle = 'white' } = this.state;
|
|
316
|
+
context.save();
|
|
317
|
+
context.translate(left, top);
|
|
318
|
+
// gauge background
|
|
319
|
+
context.beginPath();
|
|
320
|
+
context.fillStyle = fillStyle;
|
|
321
|
+
context.roundRect(0, 0, width, height, round);
|
|
322
|
+
context.fill();
|
|
323
|
+
context.clip();
|
|
324
|
+
// gauge foreground
|
|
325
|
+
context.beginPath();
|
|
326
|
+
context.fillStyle = this.statusColor;
|
|
327
|
+
context.rect(0, 0, (width / 100) * this.usage, height);
|
|
328
|
+
context.fill();
|
|
329
|
+
// high-watermark level
|
|
330
|
+
if (highWatermark && highWatermark > 0 && highWatermark < 100) {
|
|
331
|
+
context.beginPath();
|
|
332
|
+
context.strokeStyle = 'black';
|
|
333
|
+
context.lineWidth = 1;
|
|
334
|
+
context.setLineDash([3]);
|
|
335
|
+
const x = (width / 100) * highWatermark;
|
|
336
|
+
context.moveTo(x, 0);
|
|
337
|
+
context.lineTo(x, height);
|
|
338
|
+
context.stroke();
|
|
339
|
+
}
|
|
340
|
+
// low-watermark level
|
|
341
|
+
if (lowWatermark && lowWatermark > 0 && lowWatermark < 100) {
|
|
342
|
+
context.beginPath();
|
|
343
|
+
context.strokeStyle = 'black';
|
|
344
|
+
context.lineWidth = 1;
|
|
345
|
+
context.setLineDash([3]);
|
|
346
|
+
const x = (width / 100) * lowWatermark;
|
|
347
|
+
context.moveTo(x, 0);
|
|
348
|
+
context.lineTo(x, height);
|
|
349
|
+
context.stroke();
|
|
350
|
+
}
|
|
351
|
+
// gauge outline
|
|
352
|
+
context.beginPath();
|
|
353
|
+
context.setLineDash([]);
|
|
354
|
+
context.strokeStyle = strokeStyle;
|
|
355
|
+
context.lineWidth = lineWidth;
|
|
356
|
+
context.roundRect(0, 0, width, height, round);
|
|
357
|
+
context.stroke();
|
|
358
|
+
context.beginPath();
|
|
359
|
+
context.translate(-left, -top);
|
|
360
|
+
context.restore();
|
|
361
|
+
}
|
|
362
|
+
renderPortrait(context) {
|
|
363
|
+
const { left, top, width, height } = this.bounds;
|
|
364
|
+
const { highWatermark, lowWatermark, lineWidth, strokeStyle, round, fillStyle = 'white' } = this.state;
|
|
365
|
+
context.save();
|
|
366
|
+
context.translate(left, top);
|
|
367
|
+
// gauge background
|
|
368
|
+
context.beginPath();
|
|
369
|
+
context.fillStyle = fillStyle;
|
|
370
|
+
context.roundRect(0, 0, width, height, round);
|
|
371
|
+
context.fill();
|
|
372
|
+
context.clip();
|
|
373
|
+
// gauge foreground
|
|
374
|
+
context.beginPath();
|
|
375
|
+
context.fillStyle = this.statusColor;
|
|
376
|
+
context.rect(0, height - (height / 100) * this.usage, width, (height / 100) * this.usage);
|
|
377
|
+
context.fill();
|
|
378
|
+
// high-watermark level
|
|
379
|
+
if (highWatermark && highWatermark > 0 && highWatermark < 100) {
|
|
380
|
+
context.beginPath();
|
|
381
|
+
context.strokeStyle = 'black';
|
|
382
|
+
context.lineWidth = 1;
|
|
383
|
+
context.setLineDash([3]);
|
|
384
|
+
const y = height - (height / 100) * highWatermark;
|
|
385
|
+
context.moveTo(0, y);
|
|
386
|
+
context.lineTo(width, y);
|
|
387
|
+
context.stroke();
|
|
388
|
+
}
|
|
389
|
+
// low-watermark level
|
|
390
|
+
if (lowWatermark && lowWatermark > 0 && lowWatermark < 100) {
|
|
391
|
+
context.beginPath();
|
|
392
|
+
context.strokeStyle = 'black';
|
|
393
|
+
context.lineWidth = 1;
|
|
394
|
+
context.setLineDash([3]);
|
|
395
|
+
const y = height - (height / 100) * lowWatermark;
|
|
396
|
+
context.moveTo(0, y);
|
|
397
|
+
context.lineTo(width, y);
|
|
398
|
+
context.stroke();
|
|
399
|
+
}
|
|
400
|
+
// gauge outline
|
|
401
|
+
context.beginPath();
|
|
402
|
+
context.setLineDash([]);
|
|
403
|
+
context.strokeStyle = strokeStyle;
|
|
404
|
+
context.lineWidth = lineWidth;
|
|
405
|
+
context.roundRect(0, 0, width, height, round);
|
|
406
|
+
context.stroke();
|
|
407
|
+
context.beginPath();
|
|
408
|
+
context.translate(-left, -top);
|
|
409
|
+
context.restore();
|
|
410
|
+
}
|
|
411
|
+
get usage() {
|
|
412
|
+
const { currentUsage = 0, maxCapacity = 100 } = this.state;
|
|
413
|
+
return Math.round((currentUsage / maxCapacity) * 100);
|
|
414
|
+
}
|
|
415
|
+
set usage(usage) {
|
|
416
|
+
// intentionally have done nothing
|
|
417
|
+
}
|
|
418
|
+
onchangeData(after, before) {
|
|
419
|
+
const data = this.data && typeof this.data == 'object' ? this.data : {};
|
|
420
|
+
const { CURRENTCAPACITY = 0, MAXCAPACITY = 100, HIGHWATERMARK = 100, LOWWATERMARK = 0, FULLUP = 'F', CARRIER_FULL = 0, CARRIER_EMPTY = 0, CARRIER_EMPTY_EMPTY = 0, CARRIER_NORMAL = 0, CARRIER_ABNORMAL = 0 } = data;
|
|
421
|
+
const currentUsage = Number(CURRENTCAPACITY) || 0;
|
|
422
|
+
const maxCapacity = Number(MAXCAPACITY) || 100;
|
|
423
|
+
const highWatermark = Number(HIGHWATERMARK) || 100;
|
|
424
|
+
const lowWatermark = Number(LOWWATERMARK) || 0;
|
|
425
|
+
this.setState({
|
|
426
|
+
currentUsage,
|
|
427
|
+
maxCapacity,
|
|
428
|
+
highWatermark,
|
|
429
|
+
lowWatermark,
|
|
430
|
+
fullup: FULLUP == 'T',
|
|
431
|
+
usage: Math.round((currentUsage / maxCapacity) * 100),
|
|
432
|
+
carrierFull: Number(CARRIER_FULL) || 0,
|
|
433
|
+
carrierEmpty: Number(CARRIER_EMPTY) || 0,
|
|
434
|
+
carrierEmptyEmpty: Number(CARRIER_EMPTY_EMPTY) || 0,
|
|
435
|
+
carrierNormal: Number(CARRIER_NORMAL) || 0,
|
|
436
|
+
carrierAbnormal: Number(CARRIER_ABNORMAL) || 0
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
// ── hover 툴팁 (carrier/abnormal 모드 세그먼트별 수치) ───────────────────────────
|
|
440
|
+
// 색상만으로 타입 구분이 어려울 때, 마우스 hover 로 정확한 수치 확인. 뷰어 모드 전용
|
|
441
|
+
// (model-layer 가 편집 모드에선 hover dispatch 안 함).
|
|
442
|
+
async ready() {
|
|
443
|
+
await super.ready();
|
|
444
|
+
// hover 이벤트 수신 opt-in: model-layer _onmouseenter_all guard(hasLegacyHover) 통과.
|
|
445
|
+
const ev = this.model.event;
|
|
446
|
+
if (!ev || !ev.hover) {
|
|
447
|
+
;
|
|
448
|
+
this.model.event = Object.assign(Object.assign({}, (ev || {})), { hover: true });
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
onMouseEnter() {
|
|
452
|
+
// capacity 는 값이 한 개(usage%)라 툴팁 불필요. carrier/abnormal 만.
|
|
453
|
+
if (this.gaugeMode === 'capacity')
|
|
454
|
+
return;
|
|
455
|
+
this._showGaugeTooltip();
|
|
456
|
+
}
|
|
457
|
+
onMouseLeave() {
|
|
458
|
+
this._hideGaugeTooltip();
|
|
459
|
+
}
|
|
460
|
+
dispose() {
|
|
461
|
+
this._hideGaugeTooltip();
|
|
462
|
+
super.dispose();
|
|
463
|
+
}
|
|
464
|
+
_showGaugeTooltip() {
|
|
465
|
+
if (typeof document === 'undefined')
|
|
466
|
+
return;
|
|
467
|
+
const segments = this.segments(this.gaugeMode).filter(s => s.count > 0);
|
|
468
|
+
if (!segments.length)
|
|
469
|
+
return;
|
|
470
|
+
this._hideGaugeTooltip();
|
|
471
|
+
const el = document.createElement('div');
|
|
472
|
+
el.style.cssText = [
|
|
473
|
+
'position:fixed',
|
|
474
|
+
'left:-9999px',
|
|
475
|
+
'top:0',
|
|
476
|
+
'z-index:2147483647',
|
|
477
|
+
'pointer-events:none',
|
|
478
|
+
'background:rgba(30,30,30,0.92)',
|
|
479
|
+
'color:#fff',
|
|
480
|
+
'padding:6px 9px',
|
|
481
|
+
'border-radius:6px',
|
|
482
|
+
'font:12px/1.5 sans-serif',
|
|
483
|
+
'box-shadow:0 2px 10px rgba(0,0,0,0.35)',
|
|
484
|
+
'white-space:nowrap'
|
|
485
|
+
].join(';');
|
|
486
|
+
for (const seg of segments) {
|
|
487
|
+
const row = document.createElement('div');
|
|
488
|
+
row.style.cssText = 'display:flex;align-items:center;gap:6px';
|
|
489
|
+
const swatch = document.createElement('span');
|
|
490
|
+
swatch.style.cssText = `display:inline-block;width:10px;height:10px;border-radius:2px;background:${seg.color}`;
|
|
491
|
+
const label = document.createElement('span');
|
|
492
|
+
label.textContent = seg.label.replace(/\n/g, ' ');
|
|
493
|
+
label.style.cssText = 'flex:1';
|
|
494
|
+
const value = document.createElement('span');
|
|
495
|
+
value.textContent = String(seg.count);
|
|
496
|
+
value.style.cssText = 'font-weight:700;margin-left:12px';
|
|
497
|
+
row.append(swatch, label, value);
|
|
498
|
+
el.appendChild(row);
|
|
499
|
+
}
|
|
500
|
+
document.body.appendChild(el);
|
|
501
|
+
this._tooltipEl = el;
|
|
502
|
+
this._tooltipMove = (e) => {
|
|
503
|
+
el.style.left = `${e.clientX + 14}px`;
|
|
504
|
+
el.style.top = `${e.clientY + 14}px`;
|
|
505
|
+
};
|
|
506
|
+
document.addEventListener('mousemove', this._tooltipMove, true);
|
|
507
|
+
}
|
|
508
|
+
_hideGaugeTooltip() {
|
|
509
|
+
if (this._tooltipMove) {
|
|
510
|
+
document.removeEventListener('mousemove', this._tooltipMove, true);
|
|
511
|
+
this._tooltipMove = undefined;
|
|
512
|
+
}
|
|
513
|
+
if (this._tooltipEl) {
|
|
514
|
+
this._tooltipEl.remove();
|
|
515
|
+
this._tooltipEl = undefined;
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
};
|
|
519
|
+
}
|
|
520
|
+
//# sourceMappingURL=capacity-gauge-mixin.js.map
|