@sequent-org/ifc-viewer 1.2.4-ci.59.0 → 1.2.4-ci.61.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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@sequent-org/ifc-viewer",
3
3
  "private": false,
4
- "version": "1.2.4-ci.59.0",
4
+ "version": "1.2.4-ci.61.0",
5
5
  "type": "module",
6
6
  "description": "IFC 3D model viewer component for web applications - fully self-contained with local IFCLoader",
7
7
  "main": "src/index.js",
@@ -111,7 +111,18 @@
111
111
  .pointer-events-none { pointer-events: none; }
112
112
 
113
113
  /* === IFC LABEL MARKERS (UI overlay) === */
114
+ .ifc-label-actions {
115
+ position: absolute;
116
+ left: 12px;
117
+ top: 12px;
118
+ z-index: 9999;
119
+ display: flex;
120
+ gap: 6px;
121
+ pointer-events: auto;
122
+ }
123
+
114
124
  .ifc-label-add-btn,
125
+ .ifc-label-hide-btn,
115
126
  .ifc-card-add-btn {
116
127
  position: absolute;
117
128
  left: 12px;
@@ -132,10 +143,35 @@
132
143
  }
133
144
 
134
145
  .ifc-label-add-btn:hover,
146
+ .ifc-label-hide-btn:hover,
135
147
  .ifc-card-add-btn:hover {
136
148
  background: rgba(255, 255, 255, 0.98);
137
149
  }
138
150
 
151
+ .ifc-label-actions .ifc-label-add-btn,
152
+ .ifc-label-actions .ifc-label-hide-btn {
153
+ position: static;
154
+ left: auto;
155
+ top: auto;
156
+ }
157
+
158
+ .ifc-label-hide-btn--active {
159
+ background: rgba(37, 99, 235, 0.95);
160
+ border-color: rgba(37, 99, 235, 0.95);
161
+ color: #ffffff;
162
+ }
163
+
164
+ .ifc-label-hide-btn--active:hover {
165
+ background: rgba(29, 78, 216, 0.98);
166
+ border-color: rgba(29, 78, 216, 0.98);
167
+ }
168
+
169
+ .ifc-label-add-btn:disabled,
170
+ .ifc-label-add-btn--disabled {
171
+ opacity: 0.5;
172
+ cursor: not-allowed;
173
+ }
174
+
139
175
  .ifc-label-ghost,
140
176
  .ifc-label-marker,
141
177
  .ifc-card-ghost,
@@ -232,6 +268,13 @@
232
268
  background: rgba(37, 99, 235, 0.12);
233
269
  }
234
270
 
271
+ .ifc-label-menu-item--disabled,
272
+ .ifc-label-menu-item--disabled:hover {
273
+ opacity: 0.5;
274
+ cursor: not-allowed;
275
+ background: transparent;
276
+ }
277
+
235
278
  /* === NAVBAR COMPONENT === */
236
279
  .navbar {
237
280
  display: flex;
@@ -40,6 +40,7 @@ export class LabelPlacementController {
40
40
  /** @type {LabelMarker[]} */
41
41
  this._markers = [];
42
42
  this._selectedId = null;
43
+ this._labelsHidden = false;
43
44
 
44
45
  this._raycaster = new THREE.Raycaster();
45
46
  this._ndc = new THREE.Vector2();
@@ -131,6 +132,7 @@ export class LabelPlacementController {
131
132
  try { document.removeEventListener("pointerup", this._onLabelDragPointerUp); } catch (_) {}
132
133
  try { document.removeEventListener("pointercancel", this._onLabelDragPointerCancel); } catch (_) {}
133
134
  try { this._ui?.btn?.removeEventListener("click", this._onBtnClick); } catch (_) {}
135
+ try { this._ui?.hideBtn?.removeEventListener("click", this._onHideBtnClick); } catch (_) {}
134
136
  try { this._ui?.menu?.removeEventListener("pointerdown", this._onMenuPointerDown); } catch (_) {}
135
137
  try { this._ui?.menu?.removeEventListener("click", this._onMenuClick); } catch (_) {}
136
138
  try { this._ui?.canvasMenu?.removeEventListener("pointerdown", this._onCanvasMenuPointerDown); } catch (_) {}
@@ -147,13 +149,13 @@ export class LabelPlacementController {
147
149
 
148
150
  try { this._ui?.ghost?.remove?.(); } catch (_) {}
149
151
  try { this._ui?.dragGhost?.remove?.(); } catch (_) {}
150
- try { this._ui?.btn?.remove?.(); } catch (_) {}
152
+ try { this._ui?.actions?.remove?.(); } catch (_) {}
151
153
  try { this._ui?.menu?.remove?.(); } catch (_) {}
152
154
  try { this._ui?.canvasMenu?.remove?.(); } catch (_) {}
153
155
  }
154
156
 
155
157
  startPlacement() {
156
- if (this._placing) return;
158
+ if (this._placing || this._labelsHidden) return;
157
159
  this._placing = true;
158
160
 
159
161
  const controls = this.viewer?.controls;
@@ -593,11 +595,24 @@ export class LabelPlacementController {
593
595
  }
594
596
 
595
597
  #createUi() {
598
+ const actions = document.createElement("div");
599
+ actions.className = "ifc-label-actions";
600
+
596
601
  const btn = document.createElement("button");
597
602
  btn.type = "button";
598
603
  btn.className = "ifc-label-add-btn";
599
604
  btn.textContent = "+ Добавить метку";
600
605
 
606
+ const hideBtn = document.createElement("button");
607
+ hideBtn.type = "button";
608
+ hideBtn.className = "ifc-label-hide-btn";
609
+ hideBtn.textContent = "Скрыть метки";
610
+ hideBtn.setAttribute("aria-pressed", "false");
611
+ hideBtn.style.display = "none";
612
+
613
+ actions.appendChild(btn);
614
+ actions.appendChild(hideBtn);
615
+
601
616
  const ghost = document.createElement("div");
602
617
  ghost.className = "ifc-label-ghost";
603
618
  ghost.setAttribute("aria-hidden", "true");
@@ -670,12 +685,12 @@ export class LabelPlacementController {
670
685
 
671
686
  canvasMenu.appendChild(menuAdd);
672
687
 
673
- return { btn, ghost, dot, num, dragGhost, dragNum, menu, canvasMenu };
688
+ return { actions, btn, hideBtn, ghost, dot, num, dragGhost, dragNum, menu, canvasMenu, menuAdd };
674
689
  }
675
690
 
676
691
  #attachUi() {
677
692
  // Важно: container должен быть position:relative (в index.html уже так).
678
- this.container.appendChild(this._ui.btn);
693
+ this.container.appendChild(this._ui.actions);
679
694
  this.container.appendChild(this._ui.ghost);
680
695
  // Призрак перетаскивания добавляем в body, чтобы не обрезался overflow контейнера
681
696
  if (document?.body) {
@@ -699,6 +714,15 @@ export class LabelPlacementController {
699
714
  };
700
715
  this._ui.btn.addEventListener("click", this._onBtnClick, { passive: false });
701
716
 
717
+ this._onHideBtnClick = (e) => {
718
+ try { e.preventDefault(); } catch (_) {}
719
+ try { e.stopPropagation(); } catch (_) {}
720
+ try { e.stopImmediatePropagation?.(); } catch (_) {}
721
+ if (!this._markers.length) return;
722
+ this.#setLabelsHidden(!this._labelsHidden);
723
+ };
724
+ this._ui.hideBtn.addEventListener("click", this._onHideBtnClick, { passive: false });
725
+
702
726
  this._onMenuPointerDown = (e) => {
703
727
  // Не даём клику меню попасть в canvas/OrbitControls
704
728
  try { e.preventDefault(); } catch (_) {}
@@ -731,6 +755,7 @@ export class LabelPlacementController {
731
755
  const target = e.target;
732
756
  const action = target?.getAttribute?.("data-action");
733
757
  if (action !== "add") return;
758
+ if (this._labelsHidden) return;
734
759
  try { e.preventDefault(); } catch (_) {}
735
760
  try { e.stopPropagation(); } catch (_) {}
736
761
 
@@ -1281,6 +1306,7 @@ export class LabelPlacementController {
1281
1306
  sceneState: data.sceneState || null,
1282
1307
  });
1283
1308
  this._markers.push(marker);
1309
+ this.#syncHideButton();
1284
1310
 
1285
1311
  const onMarkerPointerDown = (e) => {
1286
1312
  this.logger?.log?.("[LabelClickDbg]", {
@@ -1372,6 +1398,7 @@ export class LabelPlacementController {
1372
1398
  }
1373
1399
  if (maxNumericId != null) this._nextId = Math.max(1, Math.floor(maxNumericId) + 1);
1374
1400
  if (prevSelectedId != null) this.selectLabel(prevSelectedId);
1401
+ this.#syncHideButton();
1375
1402
  }
1376
1403
 
1377
1404
  getLabelMarkers() {
@@ -1432,6 +1459,11 @@ export class LabelPlacementController {
1432
1459
  for (const m of this._markers) {
1433
1460
  if (!m || !m.el) continue;
1434
1461
 
1462
+ if (this._labelsHidden) {
1463
+ m.el.style.display = "none";
1464
+ continue;
1465
+ }
1466
+
1435
1467
  if (!model) {
1436
1468
  m.el.style.display = "none";
1437
1469
  continue;
@@ -1441,6 +1473,11 @@ export class LabelPlacementController {
1441
1473
  this._tmpV.copy(m.localPoint);
1442
1474
  model.localToWorld(this._tmpV);
1443
1475
 
1476
+ if (this.#isPointClippedBySection(this._tmpV)) {
1477
+ m.el.style.display = "none";
1478
+ continue;
1479
+ }
1480
+
1444
1481
  const ndc = this._tmpV.project(camera);
1445
1482
 
1446
1483
  // Если точка за камерой или далеко за пределами — скрываем
@@ -1462,5 +1499,54 @@ export class LabelPlacementController {
1462
1499
  m.el.style.transform = `translate3d(${x}px, ${y}px, 0) translate(-50%, -50%)`;
1463
1500
  }
1464
1501
  }
1502
+
1503
+ #isPointClippedBySection(pointWorld) {
1504
+ const planes = this.viewer?.clipping?.planes || [];
1505
+ for (const plane of planes) {
1506
+ if (!plane || !Number.isFinite(plane.constant)) continue;
1507
+ const signed = plane.distanceToPoint(pointWorld);
1508
+ if (signed < -1e-4) return true;
1509
+ }
1510
+ return false;
1511
+ }
1512
+
1513
+ #setLabelsHidden(hidden) {
1514
+ const next = !!hidden;
1515
+ if (this._labelsHidden === next) return;
1516
+ this._labelsHidden = next;
1517
+ if (this._labelsHidden) {
1518
+ try { this.cancelPlacement(); } catch (_) {}
1519
+ try { this.#closeContextMenu(); } catch (_) {}
1520
+ try { this.#closeCanvasMenu(); } catch (_) {}
1521
+ }
1522
+ this.#syncHideButton();
1523
+ }
1524
+
1525
+ #syncHideButton() {
1526
+ const btn = this._ui?.hideBtn;
1527
+ if (!btn) return;
1528
+ const hasMarkers = this._markers.length > 0;
1529
+ btn.style.display = hasMarkers ? "block" : "none";
1530
+ if (!hasMarkers && this._labelsHidden) {
1531
+ this._labelsHidden = false;
1532
+ }
1533
+ btn.setAttribute("aria-pressed", this._labelsHidden ? "true" : "false");
1534
+ try { btn.classList.toggle("ifc-label-hide-btn--active", this._labelsHidden); } catch (_) {}
1535
+ this.#syncAddAvailability();
1536
+ }
1537
+
1538
+ #syncAddAvailability() {
1539
+ const disabled = !!this._labelsHidden;
1540
+ const btn = this._ui?.btn;
1541
+ if (btn) {
1542
+ btn.disabled = disabled;
1543
+ try { btn.classList.toggle("ifc-label-add-btn--disabled", disabled); } catch (_) {}
1544
+ }
1545
+ const menuAdd = this._ui?.menuAdd;
1546
+ if (menuAdd) {
1547
+ menuAdd.disabled = disabled;
1548
+ try { menuAdd.classList.toggle("ifc-label-menu-item--disabled", disabled); } catch (_) {}
1549
+ }
1550
+ }
1465
1551
  }
1466
1552