@lv-x-software-house/x_view 1.3.5 → 1.3.6

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 (3) hide show
  1. package/dist/index.js +199 -75
  2. package/dist/index.mjs +199 -75
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -836,6 +836,55 @@ var removeLinkFromScene = (state, linkObject) => {
836
836
  else linkObject.material.dispose();
837
837
  }
838
838
  };
839
+ var removeAllTimelineElements = (state) => {
840
+ if (!state) return;
841
+ const { timelineIntervalsGroup, nodeObjects } = state;
842
+ if (timelineIntervalsGroup) {
843
+ while (timelineIntervalsGroup.children.length > 0) {
844
+ const obj = timelineIntervalsGroup.children[0];
845
+ timelineIntervalsGroup.remove(obj);
846
+ if (obj.traverse) {
847
+ obj.traverse((child) => {
848
+ if (child.geometry) child.geometry.dispose();
849
+ if (child.material) {
850
+ if (child.material.map) child.material.map.dispose();
851
+ child.material.dispose();
852
+ }
853
+ });
854
+ } else {
855
+ if (obj.geometry) obj.geometry.dispose();
856
+ if (obj.material) {
857
+ if (obj.material.map) obj.material.map.dispose();
858
+ obj.material.dispose();
859
+ }
860
+ }
861
+ }
862
+ }
863
+ if (nodeObjects) {
864
+ Object.values(nodeObjects).forEach((mesh) => {
865
+ var _a, _b, _c, _d;
866
+ if (!mesh) return;
867
+ const oldTimelineLabel = mesh.getObjectByName("timelineLabel");
868
+ if (oldTimelineLabel) {
869
+ mesh.remove(oldTimelineLabel);
870
+ if ((_a = oldTimelineLabel.material) == null ? void 0 : _a.map) {
871
+ oldTimelineLabel.material.map.dispose();
872
+ }
873
+ (_b = oldTimelineLabel.material) == null ? void 0 : _b.dispose();
874
+ }
875
+ const oldQuestDateLabel = mesh.getObjectByName("questDateLabel");
876
+ if (oldQuestDateLabel) {
877
+ mesh.remove(oldQuestDateLabel);
878
+ if ((_c = oldQuestDateLabel.material) == null ? void 0 : _c.map) {
879
+ oldQuestDateLabel.material.map.dispose();
880
+ }
881
+ (_d = oldQuestDateLabel.material) == null ? void 0 : _d.dispose();
882
+ }
883
+ delete mesh.userData.timelineIntervalBar;
884
+ delete mesh.userData.timelineEndLabel;
885
+ });
886
+ }
887
+ };
839
888
  var removeNodeFromScene = (state, nodeId) => {
840
889
  const {
841
890
  graphGroup,
@@ -864,8 +913,15 @@ var removeNodeFromScene = (state, nodeId) => {
864
913
  const { timelineIntervalBar, timelineEndLabel } = nodeToRemove.userData;
865
914
  if (timelineIntervalBar) {
866
915
  timelineIntervalsGroup.remove(timelineIntervalBar);
867
- if (timelineIntervalBar.geometry) timelineIntervalBar.geometry.dispose();
868
- if (timelineIntervalBar.material) timelineIntervalBar.material.dispose();
916
+ if (timelineIntervalBar.traverse) {
917
+ timelineIntervalBar.traverse((child) => {
918
+ if (child.geometry) child.geometry.dispose();
919
+ if (child.material) child.material.dispose();
920
+ });
921
+ } else {
922
+ if (timelineIntervalBar.geometry) timelineIntervalBar.geometry.dispose();
923
+ if (timelineIntervalBar.material) timelineIntervalBar.material.dispose();
924
+ }
869
925
  }
870
926
  if (timelineEndLabel) {
871
927
  timelineIntervalsGroup.remove(timelineEndLabel);
@@ -2177,6 +2233,7 @@ var userActionHandlers = {
2177
2233
  if (!nodeData || !sceneDataRef.current) return;
2178
2234
  const nodeIdToDismiss = nodeData.id;
2179
2235
  const strNodeId = String(nodeIdToDismiss);
2236
+ removeAllTimelineElements(stateRef.current);
2180
2237
  const { ancestryGroup, ancestryLinks } = stateRef.current;
2181
2238
  if (ancestryGroup && ancestryLinks) {
2182
2239
  const remainingAncestryLinks = [];
@@ -2208,6 +2265,7 @@ var userActionHandlers = {
2208
2265
  (id) => id !== strNodeIdToKeep
2209
2266
  );
2210
2267
  if (nodeIdsToDismiss.length === 0) return;
2268
+ removeAllTimelineElements(stateRef.current);
2211
2269
  const { ancestryGroup, ancestryLinks } = stateRef.current;
2212
2270
  if (ancestryGroup && ancestryLinks) {
2213
2271
  ancestryLinks.forEach((link) => {
@@ -2230,6 +2288,7 @@ var userActionHandlers = {
2230
2288
  setters.setMultiContextMenu({ visible: false });
2231
2289
  if (!nodeIds || nodeIds.size === 0 || !sceneDataRef.current) return;
2232
2290
  const strNodeIds = Array.from(nodeIds).map(String);
2291
+ removeAllTimelineElements(stateRef.current);
2233
2292
  const { ancestryGroup, ancestryLinks } = stateRef.current;
2234
2293
  if (ancestryGroup && ancestryLinks) {
2235
2294
  const remainingAncestryLinks = [];
@@ -2269,6 +2328,7 @@ var userActionHandlers = {
2269
2328
  stateRef.current.selectedNodes.clear();
2270
2329
  return;
2271
2330
  }
2331
+ removeAllTimelineElements(stateRef.current);
2272
2332
  const { ancestryGroup, ancestryLinks } = stateRef.current;
2273
2333
  if (ancestryGroup && ancestryLinks) {
2274
2334
  const remainingAncestryLinks = [];
@@ -4749,6 +4809,11 @@ function calculateNextRecurrenceDate(config) {
4749
4809
  confirmed: true
4750
4810
  };
4751
4811
  }
4812
+ var createSafeDate = (year, month = 0, day = 1) => {
4813
+ const d = /* @__PURE__ */ new Date();
4814
+ d.setFullYear(year, month, day);
4815
+ return d;
4816
+ };
4752
4817
  var parseDate = (str) => {
4753
4818
  if (!str) return null;
4754
4819
  const parts = str.split("/");
@@ -4757,7 +4822,7 @@ var parseDate = (str) => {
4757
4822
  const month = parseInt(parts[1], 10) - 1;
4758
4823
  const year = parseInt(parts[2], 10);
4759
4824
  if (!isNaN(day) && !isNaN(month) && !isNaN(year)) {
4760
- return new Date(year, month, day);
4825
+ return createSafeDate(year, month, day);
4761
4826
  }
4762
4827
  }
4763
4828
  return null;
@@ -4812,7 +4877,7 @@ var parseDateProp = (dateProp) => {
4812
4877
  } else if (dateProp.year) {
4813
4878
  const y = parseInt(dateProp.year, 10);
4814
4879
  if (!isNaN(y)) {
4815
- const d = new Date(y, 0, 1);
4880
+ const d = createSafeDate(y, 0, 1);
4816
4881
  info = {
4817
4882
  startDate: d,
4818
4883
  endDate: d,
@@ -4825,8 +4890,8 @@ var parseDateProp = (dateProp) => {
4825
4890
  const sY = parseInt(dateProp.date_interval.start, 10);
4826
4891
  const eY = parseInt(dateProp.date_interval.end, 10) || sY;
4827
4892
  if (!isNaN(sY)) {
4828
- const sd = new Date(sY, 0, 1);
4829
- const ed = new Date(eY, 11, 31);
4893
+ const sd = createSafeDate(sY, 0, 1);
4894
+ const ed = createSafeDate(eY, 11, 31);
4830
4895
  info = {
4831
4896
  startDate: sd,
4832
4897
  endDate: ed,
@@ -19355,7 +19420,62 @@ var TIMELINE_LAYER_SPACING_Y = 25;
19355
19420
  var TIMELINE_NODE_SPACING_X = 20;
19356
19421
  var TIMELINE_INTERVAL_BAR_RADIUS = 0.2;
19357
19422
  var TIMELINE_INTERVAL_BAR_OPACITY = 0.85;
19358
- var TIMELINE_GOLD_COLOR = 16766720;
19423
+ var TIMELINE_BLUE_COLOR = 30719;
19424
+ var TIMELINE_BRACKET_WIDTH = 4;
19425
+ var createTimelineIntervalBracket = () => {
19426
+ const group = new THREE3.Group();
19427
+ group.name = "timelineIntervalBracket";
19428
+ const barMaterial = new THREE3.MeshStandardMaterial({
19429
+ color: TIMELINE_BLUE_COLOR,
19430
+ emissive: 17578,
19431
+ emissiveIntensity: 0.5,
19432
+ transparent: true,
19433
+ opacity: TIMELINE_INTERVAL_BAR_OPACITY,
19434
+ roughness: 0.3,
19435
+ metalness: 0.8
19436
+ });
19437
+ const geoHorizontal = new THREE3.CylinderGeometry(
19438
+ TIMELINE_INTERVAL_BAR_RADIUS,
19439
+ TIMELINE_INTERVAL_BAR_RADIUS,
19440
+ 1,
19441
+ 16
19442
+ );
19443
+ const geoVertical = new THREE3.CylinderGeometry(
19444
+ TIMELINE_INTERVAL_BAR_RADIUS,
19445
+ TIMELINE_INTERVAL_BAR_RADIUS,
19446
+ 1,
19447
+ 16
19448
+ );
19449
+ const bottomArm = new THREE3.Mesh(geoHorizontal, barMaterial);
19450
+ bottomArm.rotation.z = Math.PI / 2;
19451
+ const verticalStem = new THREE3.Mesh(geoVertical, barMaterial);
19452
+ const topArm = new THREE3.Mesh(geoHorizontal.clone(), barMaterial);
19453
+ topArm.rotation.z = Math.PI / 2;
19454
+ group.add(bottomArm);
19455
+ group.add(verticalStem);
19456
+ group.add(topArm);
19457
+ group.userData.bottomArm = bottomArm;
19458
+ group.userData.verticalStem = verticalStem;
19459
+ group.userData.topArm = topArm;
19460
+ return group;
19461
+ };
19462
+ var updateTimelineIntervalBracket = (group, startPos, endPos) => {
19463
+ if (!group || !group.userData.bottomArm) return;
19464
+ const { bottomArm, verticalStem, topArm } = group.userData;
19465
+ const w = TIMELINE_BRACKET_WIDTH;
19466
+ const z = -0.5;
19467
+ const p1 = new THREE3.Vector3(startPos.x, startPos.y, z);
19468
+ const p2 = new THREE3.Vector3(startPos.x - w, startPos.y, z);
19469
+ const p3 = new THREE3.Vector3(endPos.x - w, endPos.y, z);
19470
+ const p4 = new THREE3.Vector3(endPos.x, endPos.y, z);
19471
+ bottomArm.scale.y = w;
19472
+ bottomArm.position.set((p1.x + p2.x) / 2, p1.y, z);
19473
+ const verticalLen = Math.abs(p3.y - p2.y);
19474
+ verticalStem.scale.y = Math.max(verticalLen, 1e-3);
19475
+ verticalStem.position.set(p2.x, (p2.y + p3.y) / 2, z);
19476
+ topArm.scale.y = w;
19477
+ topArm.position.set((p3.x + p4.x) / 2, p3.y, z);
19478
+ };
19359
19479
  var QUEST_LENS_COLUMN_SPACING = 80;
19360
19480
  var QUEST_LENS_ROW_SPACING = 40;
19361
19481
  var getAllNodeIdsFromTree = (treeNode) => {
@@ -20044,7 +20164,9 @@ function XViewScene({
20044
20164
  const month = parseInt(parts[1], 10) - 1;
20045
20165
  const year = parseInt(parts[2], 10);
20046
20166
  if (!isNaN(day) && !isNaN(month) && !isNaN(year)) {
20047
- return new Date(year, month, day);
20167
+ const d = /* @__PURE__ */ new Date();
20168
+ d.setFullYear(year, month, day);
20169
+ return d;
20048
20170
  }
20049
20171
  }
20050
20172
  return null;
@@ -20101,10 +20223,12 @@ function XViewScene({
20101
20223
  });
20102
20224
  const timelineWidth = (columns.length > 0 ? columns.length - 1 : 0) * TIMELINE_NODE_SPACING_X;
20103
20225
  const offsetX = -timelineWidth / 2;
20226
+ const allFinalPoints = [];
20104
20227
  timelineNodes.forEach((node) => {
20105
20228
  const { mesh, type, startDate, endDate, label, columnIndex, isUndated } = node;
20106
20229
  const x = columnIndex * TIMELINE_NODE_SPACING_X + offsetX;
20107
20230
  const y = isUndated ? 0 : timeToYMap.get(startDate.getTime());
20231
+ allFinalPoints.push(new THREE3.Vector3(x, y, 0));
20108
20232
  const timelineTween = new import_tween2.Tween(mesh.position).to({ x, y, z: 0 }, 1500).easing(import_tween2.Easing.Cubic.InOut);
20109
20233
  tweenGroup.add(timelineTween);
20110
20234
  timelineTween.start();
@@ -20117,25 +20241,16 @@ function XViewScene({
20117
20241
  if (type === "interval") {
20118
20242
  const endY = timeToYMap.get(endDate.getTime());
20119
20243
  if (endY > y) {
20120
- const barGeometry = new THREE3.CylinderGeometry(
20121
- TIMELINE_INTERVAL_BAR_RADIUS,
20122
- TIMELINE_INTERVAL_BAR_RADIUS,
20123
- 1,
20124
- 16
20125
- );
20126
- const barMaterial = new THREE3.MeshStandardMaterial({
20127
- color: TIMELINE_GOLD_COLOR,
20128
- emissive: 12092939,
20129
- emissiveIntensity: 0.4,
20130
- transparent: true,
20131
- opacity: TIMELINE_INTERVAL_BAR_OPACITY,
20132
- roughness: 0.3,
20133
- metalness: 0.8
20134
- });
20135
- const intervalBar = new THREE3.Mesh(barGeometry, barMaterial);
20244
+ allFinalPoints.push(new THREE3.Vector3(x, endY, 0));
20245
+ const intervalBar = createTimelineIntervalBracket();
20136
20246
  intervalBar.userData.associatedNodeId = mesh.userData.id;
20137
20247
  const endPosition = new THREE3.Vector3(x, endY, -0.5);
20138
20248
  intervalBar.userData.endPosition = endPosition;
20249
+ updateTimelineIntervalBracket(
20250
+ intervalBar,
20251
+ new THREE3.Vector3(x, y, 0),
20252
+ endPosition
20253
+ );
20139
20254
  timelineIntervalsGroup.add(intervalBar);
20140
20255
  const endLabelText = endDate.getFullYear().toString();
20141
20256
  const endLabel = createTextSprite(endLabelText, 48);
@@ -20148,7 +20263,21 @@ function XViewScene({
20148
20263
  }
20149
20264
  }
20150
20265
  });
20151
- }, []);
20266
+ if (allFinalPoints.length > 0) {
20267
+ const boundingBox = new THREE3.Box3().setFromPoints(allFinalPoints);
20268
+ const center = new THREE3.Vector3();
20269
+ boundingBox.getCenter(center);
20270
+ const size = new THREE3.Vector3();
20271
+ boundingBox.getSize(size);
20272
+ const spread = Math.max(size.x, size.y, size.z, 1);
20273
+ const desiredDistance = Math.max(
20274
+ x_view_config.CAMERA_ZOOM_DISTANCE,
20275
+ spread * 0.75
20276
+ );
20277
+ const zoomFactor = x_view_config.CAMERA_ZOOM_DISTANCE / desiredDistance;
20278
+ tweenToTarget(center, zoomFactor);
20279
+ }
20280
+ }, [tweenToTarget]);
20152
20281
  const handleVersionTimeline = (0, import_react37.useCallback)(
20153
20282
  (sourceMesh, versionMeshes) => {
20154
20283
  const { tweenGroup, timelineIntervalsGroup } = stateRef.current;
@@ -20184,7 +20313,9 @@ function XViewScene({
20184
20313
  const month = parseInt(parts[1], 10) - 1;
20185
20314
  const year = parseInt(parts[2], 10);
20186
20315
  if (!isNaN(day) && !isNaN(month) && !isNaN(year)) {
20187
- return new Date(year, month, day);
20316
+ const d = /* @__PURE__ */ new Date();
20317
+ d.setFullYear(year, month, day);
20318
+ return d;
20188
20319
  }
20189
20320
  }
20190
20321
  return null;
@@ -20255,22 +20386,7 @@ function XViewScene({
20255
20386
  if (type === "interval") {
20256
20387
  const relativeEndY = timeToYMap.get(endDate.getTime()) - timeToYMap.get(startDate.getTime());
20257
20388
  if (relativeEndY > 0) {
20258
- const barGeometry = new THREE3.CylinderGeometry(
20259
- TIMELINE_INTERVAL_BAR_RADIUS,
20260
- TIMELINE_INTERVAL_BAR_RADIUS,
20261
- 1,
20262
- 16
20263
- );
20264
- const barMaterial = new THREE3.MeshStandardMaterial({
20265
- color: TIMELINE_GOLD_COLOR,
20266
- emissive: 12092939,
20267
- emissiveIntensity: 0.4,
20268
- transparent: true,
20269
- opacity: TIMELINE_INTERVAL_BAR_OPACITY,
20270
- roughness: 0.3,
20271
- metalness: 0.8
20272
- });
20273
- const intervalBar = new THREE3.Mesh(barGeometry, barMaterial);
20389
+ const intervalBar = createTimelineIntervalBracket();
20274
20390
  intervalBar.userData.associatedNodeId = mesh.userData.id;
20275
20391
  const endPosition = new THREE3.Vector3(
20276
20392
  baseX,
@@ -20278,6 +20394,11 @@ function XViewScene({
20278
20394
  -0.5
20279
20395
  );
20280
20396
  intervalBar.userData.endPosition = endPosition;
20397
+ updateTimelineIntervalBracket(
20398
+ intervalBar,
20399
+ new THREE3.Vector3(x, y, 0),
20400
+ endPosition
20401
+ );
20281
20402
  timelineIntervalsGroup.add(intervalBar);
20282
20403
  const endLabelText = endDate.getFullYear().toString();
20283
20404
  const endLabel = createTextSprite(endLabelText, 48);
@@ -20773,25 +20894,15 @@ function XViewScene({
20773
20894
  const endY = timeToYMap.get(timeInfo.endDate.getTime());
20774
20895
  if (Math.abs(endY - targetY) > 0.1) {
20775
20896
  const barHeight = Math.abs(endY - targetY);
20776
- const barGeometry = new THREE3.CylinderGeometry(
20777
- TIMELINE_INTERVAL_BAR_RADIUS,
20778
- TIMELINE_INTERVAL_BAR_RADIUS,
20779
- 1,
20780
- 16
20781
- );
20782
- const barMaterial = new THREE3.MeshStandardMaterial({
20783
- color: TIMELINE_GOLD_COLOR,
20784
- emissive: 12092939,
20785
- emissiveIntensity: 0.4,
20786
- transparent: true,
20787
- opacity: TIMELINE_INTERVAL_BAR_OPACITY,
20788
- roughness: 0.3,
20789
- metalness: 0.8
20790
- });
20791
- const intervalBar = new THREE3.Mesh(barGeometry, barMaterial);
20897
+ const intervalBar = createTimelineIntervalBracket();
20792
20898
  intervalBar.userData.associatedNodeId = mesh.userData.id;
20793
- intervalBar.position.set(finalX, (targetY + endY) / 2, -0.5);
20794
- intervalBar.scale.y = barHeight;
20899
+ const endPosition = new THREE3.Vector3(finalX, endY, -0.5);
20900
+ intervalBar.userData.endPosition = endPosition;
20901
+ updateTimelineIntervalBracket(
20902
+ intervalBar,
20903
+ new THREE3.Vector3(finalX, targetY, 0),
20904
+ endPosition
20905
+ );
20795
20906
  graphGroup.add(intervalBar);
20796
20907
  mesh.userData.timelineIntervalBar = intervalBar;
20797
20908
  const endLabel = createTextSprite(
@@ -21560,23 +21671,12 @@ function XViewScene({
21560
21671
  }
21561
21672
  });
21562
21673
  (_a2 = stateRef.current.timelineIntervalsGroup) == null ? void 0 : _a2.children.forEach((obj) => {
21563
- if (obj.isMesh && obj.userData.associatedNodeId && obj.userData.endPosition) {
21674
+ if (obj.userData.associatedNodeId && obj.userData.endPosition) {
21564
21675
  const bar = obj;
21565
21676
  const node = stateRef.current.nodeObjects[String(bar.userData.associatedNodeId)];
21566
21677
  const endPos = bar.userData.endPosition;
21567
21678
  if (node) {
21568
- const startPos = node.position;
21569
- const distance = startPos.distanceTo(endPos);
21570
- bar.scale.y = distance;
21571
- const midpoint = new THREE3.Vector3().lerpVectors(
21572
- startPos,
21573
- endPos,
21574
- 0.5
21575
- );
21576
- bar.position.copy(midpoint);
21577
- const direction = new THREE3.Vector3().subVectors(endPos, startPos).normalize();
21578
- const upVector = new THREE3.Vector3(0, 1, 0);
21579
- bar.quaternion.setFromUnitVectors(upVector, direction);
21679
+ updateTimelineIntervalBracket(bar, node.position, endPos);
21580
21680
  }
21581
21681
  }
21582
21682
  });
@@ -24558,8 +24658,9 @@ function XViewScene({
24558
24658
  )) == null ? void 0 : _b.parentFileId : null;
24559
24659
  const detailsNodeDatasetInfo = detailsNode ? stateRef.current.nodeIdToParentFileMap.get(String(detailsNode.id)) : null;
24560
24660
  (0, import_react37.useEffect)(() => {
24561
- if (isInitialized && focusNodeId && !hasFocusedInitial) {
24562
- const nodeObjects = stateRef.current.nodeObjects || {};
24661
+ if (!isInitialized || hasFocusedInitial) return;
24662
+ const nodeObjects = stateRef.current.nodeObjects || {};
24663
+ if (focusNodeId) {
24563
24664
  const targetMesh = nodeObjects[String(focusNodeId)];
24564
24665
  if (targetMesh) {
24565
24666
  setTimeout(() => {
@@ -24572,11 +24673,34 @@ function XViewScene({
24572
24673
  "O link aponta para um item que n\xE3o foi encontrado ou foi exclu\xEDdo."
24573
24674
  );
24574
24675
  }
24676
+ } else if (!focusAncestryId) {
24677
+ const meshes = Object.values(nodeObjects);
24678
+ if (meshes.length > 0) {
24679
+ setTimeout(() => {
24680
+ const nodePositions = meshes.map((mesh) => mesh.position.clone());
24681
+ const boundingBox = new THREE3.Box3().setFromPoints(nodePositions);
24682
+ const center = new THREE3.Vector3();
24683
+ boundingBox.getCenter(center);
24684
+ const size = new THREE3.Vector3();
24685
+ boundingBox.getSize(size);
24686
+ const maxDim = Math.max(size.x, size.y, size.z);
24687
+ const desiredDistance = Math.max(
24688
+ x_view_config.CAMERA_ZOOM_DISTANCE,
24689
+ maxDim * 1.8
24690
+ );
24691
+ const zoomFactor = x_view_config.CAMERA_ZOOM_DISTANCE / desiredDistance;
24692
+ tweenToTarget(center, zoomFactor);
24693
+ setHasFocusedInitial(true);
24694
+ }, 300);
24695
+ } else {
24696
+ setHasFocusedInitial(true);
24697
+ }
24575
24698
  }
24576
24699
  }, [
24577
24700
  isInitialized,
24578
24701
  sceneVersion,
24579
24702
  focusNodeId,
24703
+ focusAncestryId,
24580
24704
  hasFocusedInitial,
24581
24705
  tweenToTarget
24582
24706
  ]);
package/dist/index.mjs CHANGED
@@ -797,6 +797,55 @@ var removeLinkFromScene = (state, linkObject) => {
797
797
  else linkObject.material.dispose();
798
798
  }
799
799
  };
800
+ var removeAllTimelineElements = (state) => {
801
+ if (!state) return;
802
+ const { timelineIntervalsGroup, nodeObjects } = state;
803
+ if (timelineIntervalsGroup) {
804
+ while (timelineIntervalsGroup.children.length > 0) {
805
+ const obj = timelineIntervalsGroup.children[0];
806
+ timelineIntervalsGroup.remove(obj);
807
+ if (obj.traverse) {
808
+ obj.traverse((child) => {
809
+ if (child.geometry) child.geometry.dispose();
810
+ if (child.material) {
811
+ if (child.material.map) child.material.map.dispose();
812
+ child.material.dispose();
813
+ }
814
+ });
815
+ } else {
816
+ if (obj.geometry) obj.geometry.dispose();
817
+ if (obj.material) {
818
+ if (obj.material.map) obj.material.map.dispose();
819
+ obj.material.dispose();
820
+ }
821
+ }
822
+ }
823
+ }
824
+ if (nodeObjects) {
825
+ Object.values(nodeObjects).forEach((mesh) => {
826
+ var _a, _b, _c, _d;
827
+ if (!mesh) return;
828
+ const oldTimelineLabel = mesh.getObjectByName("timelineLabel");
829
+ if (oldTimelineLabel) {
830
+ mesh.remove(oldTimelineLabel);
831
+ if ((_a = oldTimelineLabel.material) == null ? void 0 : _a.map) {
832
+ oldTimelineLabel.material.map.dispose();
833
+ }
834
+ (_b = oldTimelineLabel.material) == null ? void 0 : _b.dispose();
835
+ }
836
+ const oldQuestDateLabel = mesh.getObjectByName("questDateLabel");
837
+ if (oldQuestDateLabel) {
838
+ mesh.remove(oldQuestDateLabel);
839
+ if ((_c = oldQuestDateLabel.material) == null ? void 0 : _c.map) {
840
+ oldQuestDateLabel.material.map.dispose();
841
+ }
842
+ (_d = oldQuestDateLabel.material) == null ? void 0 : _d.dispose();
843
+ }
844
+ delete mesh.userData.timelineIntervalBar;
845
+ delete mesh.userData.timelineEndLabel;
846
+ });
847
+ }
848
+ };
800
849
  var removeNodeFromScene = (state, nodeId) => {
801
850
  const {
802
851
  graphGroup,
@@ -825,8 +874,15 @@ var removeNodeFromScene = (state, nodeId) => {
825
874
  const { timelineIntervalBar, timelineEndLabel } = nodeToRemove.userData;
826
875
  if (timelineIntervalBar) {
827
876
  timelineIntervalsGroup.remove(timelineIntervalBar);
828
- if (timelineIntervalBar.geometry) timelineIntervalBar.geometry.dispose();
829
- if (timelineIntervalBar.material) timelineIntervalBar.material.dispose();
877
+ if (timelineIntervalBar.traverse) {
878
+ timelineIntervalBar.traverse((child) => {
879
+ if (child.geometry) child.geometry.dispose();
880
+ if (child.material) child.material.dispose();
881
+ });
882
+ } else {
883
+ if (timelineIntervalBar.geometry) timelineIntervalBar.geometry.dispose();
884
+ if (timelineIntervalBar.material) timelineIntervalBar.material.dispose();
885
+ }
830
886
  }
831
887
  if (timelineEndLabel) {
832
888
  timelineIntervalsGroup.remove(timelineEndLabel);
@@ -2138,6 +2194,7 @@ var userActionHandlers = {
2138
2194
  if (!nodeData || !sceneDataRef.current) return;
2139
2195
  const nodeIdToDismiss = nodeData.id;
2140
2196
  const strNodeId = String(nodeIdToDismiss);
2197
+ removeAllTimelineElements(stateRef.current);
2141
2198
  const { ancestryGroup, ancestryLinks } = stateRef.current;
2142
2199
  if (ancestryGroup && ancestryLinks) {
2143
2200
  const remainingAncestryLinks = [];
@@ -2169,6 +2226,7 @@ var userActionHandlers = {
2169
2226
  (id) => id !== strNodeIdToKeep
2170
2227
  );
2171
2228
  if (nodeIdsToDismiss.length === 0) return;
2229
+ removeAllTimelineElements(stateRef.current);
2172
2230
  const { ancestryGroup, ancestryLinks } = stateRef.current;
2173
2231
  if (ancestryGroup && ancestryLinks) {
2174
2232
  ancestryLinks.forEach((link) => {
@@ -2191,6 +2249,7 @@ var userActionHandlers = {
2191
2249
  setters.setMultiContextMenu({ visible: false });
2192
2250
  if (!nodeIds || nodeIds.size === 0 || !sceneDataRef.current) return;
2193
2251
  const strNodeIds = Array.from(nodeIds).map(String);
2252
+ removeAllTimelineElements(stateRef.current);
2194
2253
  const { ancestryGroup, ancestryLinks } = stateRef.current;
2195
2254
  if (ancestryGroup && ancestryLinks) {
2196
2255
  const remainingAncestryLinks = [];
@@ -2230,6 +2289,7 @@ var userActionHandlers = {
2230
2289
  stateRef.current.selectedNodes.clear();
2231
2290
  return;
2232
2291
  }
2292
+ removeAllTimelineElements(stateRef.current);
2233
2293
  const { ancestryGroup, ancestryLinks } = stateRef.current;
2234
2294
  if (ancestryGroup && ancestryLinks) {
2235
2295
  const remainingAncestryLinks = [];
@@ -4710,6 +4770,11 @@ function calculateNextRecurrenceDate(config) {
4710
4770
  confirmed: true
4711
4771
  };
4712
4772
  }
4773
+ var createSafeDate = (year, month = 0, day = 1) => {
4774
+ const d = /* @__PURE__ */ new Date();
4775
+ d.setFullYear(year, month, day);
4776
+ return d;
4777
+ };
4713
4778
  var parseDate = (str) => {
4714
4779
  if (!str) return null;
4715
4780
  const parts = str.split("/");
@@ -4718,7 +4783,7 @@ var parseDate = (str) => {
4718
4783
  const month = parseInt(parts[1], 10) - 1;
4719
4784
  const year = parseInt(parts[2], 10);
4720
4785
  if (!isNaN(day) && !isNaN(month) && !isNaN(year)) {
4721
- return new Date(year, month, day);
4786
+ return createSafeDate(year, month, day);
4722
4787
  }
4723
4788
  }
4724
4789
  return null;
@@ -4773,7 +4838,7 @@ var parseDateProp = (dateProp) => {
4773
4838
  } else if (dateProp.year) {
4774
4839
  const y = parseInt(dateProp.year, 10);
4775
4840
  if (!isNaN(y)) {
4776
- const d = new Date(y, 0, 1);
4841
+ const d = createSafeDate(y, 0, 1);
4777
4842
  info = {
4778
4843
  startDate: d,
4779
4844
  endDate: d,
@@ -4786,8 +4851,8 @@ var parseDateProp = (dateProp) => {
4786
4851
  const sY = parseInt(dateProp.date_interval.start, 10);
4787
4852
  const eY = parseInt(dateProp.date_interval.end, 10) || sY;
4788
4853
  if (!isNaN(sY)) {
4789
- const sd = new Date(sY, 0, 1);
4790
- const ed = new Date(eY, 11, 31);
4854
+ const sd = createSafeDate(sY, 0, 1);
4855
+ const ed = createSafeDate(eY, 11, 31);
4791
4856
  info = {
4792
4857
  startDate: sd,
4793
4858
  endDate: ed,
@@ -19447,7 +19512,62 @@ var TIMELINE_LAYER_SPACING_Y = 25;
19447
19512
  var TIMELINE_NODE_SPACING_X = 20;
19448
19513
  var TIMELINE_INTERVAL_BAR_RADIUS = 0.2;
19449
19514
  var TIMELINE_INTERVAL_BAR_OPACITY = 0.85;
19450
- var TIMELINE_GOLD_COLOR = 16766720;
19515
+ var TIMELINE_BLUE_COLOR = 30719;
19516
+ var TIMELINE_BRACKET_WIDTH = 4;
19517
+ var createTimelineIntervalBracket = () => {
19518
+ const group = new THREE3.Group();
19519
+ group.name = "timelineIntervalBracket";
19520
+ const barMaterial = new THREE3.MeshStandardMaterial({
19521
+ color: TIMELINE_BLUE_COLOR,
19522
+ emissive: 17578,
19523
+ emissiveIntensity: 0.5,
19524
+ transparent: true,
19525
+ opacity: TIMELINE_INTERVAL_BAR_OPACITY,
19526
+ roughness: 0.3,
19527
+ metalness: 0.8
19528
+ });
19529
+ const geoHorizontal = new THREE3.CylinderGeometry(
19530
+ TIMELINE_INTERVAL_BAR_RADIUS,
19531
+ TIMELINE_INTERVAL_BAR_RADIUS,
19532
+ 1,
19533
+ 16
19534
+ );
19535
+ const geoVertical = new THREE3.CylinderGeometry(
19536
+ TIMELINE_INTERVAL_BAR_RADIUS,
19537
+ TIMELINE_INTERVAL_BAR_RADIUS,
19538
+ 1,
19539
+ 16
19540
+ );
19541
+ const bottomArm = new THREE3.Mesh(geoHorizontal, barMaterial);
19542
+ bottomArm.rotation.z = Math.PI / 2;
19543
+ const verticalStem = new THREE3.Mesh(geoVertical, barMaterial);
19544
+ const topArm = new THREE3.Mesh(geoHorizontal.clone(), barMaterial);
19545
+ topArm.rotation.z = Math.PI / 2;
19546
+ group.add(bottomArm);
19547
+ group.add(verticalStem);
19548
+ group.add(topArm);
19549
+ group.userData.bottomArm = bottomArm;
19550
+ group.userData.verticalStem = verticalStem;
19551
+ group.userData.topArm = topArm;
19552
+ return group;
19553
+ };
19554
+ var updateTimelineIntervalBracket = (group, startPos, endPos) => {
19555
+ if (!group || !group.userData.bottomArm) return;
19556
+ const { bottomArm, verticalStem, topArm } = group.userData;
19557
+ const w = TIMELINE_BRACKET_WIDTH;
19558
+ const z = -0.5;
19559
+ const p1 = new THREE3.Vector3(startPos.x, startPos.y, z);
19560
+ const p2 = new THREE3.Vector3(startPos.x - w, startPos.y, z);
19561
+ const p3 = new THREE3.Vector3(endPos.x - w, endPos.y, z);
19562
+ const p4 = new THREE3.Vector3(endPos.x, endPos.y, z);
19563
+ bottomArm.scale.y = w;
19564
+ bottomArm.position.set((p1.x + p2.x) / 2, p1.y, z);
19565
+ const verticalLen = Math.abs(p3.y - p2.y);
19566
+ verticalStem.scale.y = Math.max(verticalLen, 1e-3);
19567
+ verticalStem.position.set(p2.x, (p2.y + p3.y) / 2, z);
19568
+ topArm.scale.y = w;
19569
+ topArm.position.set((p3.x + p4.x) / 2, p3.y, z);
19570
+ };
19451
19571
  var QUEST_LENS_COLUMN_SPACING = 80;
19452
19572
  var QUEST_LENS_ROW_SPACING = 40;
19453
19573
  var getAllNodeIdsFromTree = (treeNode) => {
@@ -20136,7 +20256,9 @@ function XViewScene({
20136
20256
  const month = parseInt(parts[1], 10) - 1;
20137
20257
  const year = parseInt(parts[2], 10);
20138
20258
  if (!isNaN(day) && !isNaN(month) && !isNaN(year)) {
20139
- return new Date(year, month, day);
20259
+ const d = /* @__PURE__ */ new Date();
20260
+ d.setFullYear(year, month, day);
20261
+ return d;
20140
20262
  }
20141
20263
  }
20142
20264
  return null;
@@ -20193,10 +20315,12 @@ function XViewScene({
20193
20315
  });
20194
20316
  const timelineWidth = (columns.length > 0 ? columns.length - 1 : 0) * TIMELINE_NODE_SPACING_X;
20195
20317
  const offsetX = -timelineWidth / 2;
20318
+ const allFinalPoints = [];
20196
20319
  timelineNodes.forEach((node) => {
20197
20320
  const { mesh, type, startDate, endDate, label, columnIndex, isUndated } = node;
20198
20321
  const x = columnIndex * TIMELINE_NODE_SPACING_X + offsetX;
20199
20322
  const y = isUndated ? 0 : timeToYMap.get(startDate.getTime());
20323
+ allFinalPoints.push(new THREE3.Vector3(x, y, 0));
20200
20324
  const timelineTween = new Tween2(mesh.position).to({ x, y, z: 0 }, 1500).easing(Easing2.Cubic.InOut);
20201
20325
  tweenGroup.add(timelineTween);
20202
20326
  timelineTween.start();
@@ -20209,25 +20333,16 @@ function XViewScene({
20209
20333
  if (type === "interval") {
20210
20334
  const endY = timeToYMap.get(endDate.getTime());
20211
20335
  if (endY > y) {
20212
- const barGeometry = new THREE3.CylinderGeometry(
20213
- TIMELINE_INTERVAL_BAR_RADIUS,
20214
- TIMELINE_INTERVAL_BAR_RADIUS,
20215
- 1,
20216
- 16
20217
- );
20218
- const barMaterial = new THREE3.MeshStandardMaterial({
20219
- color: TIMELINE_GOLD_COLOR,
20220
- emissive: 12092939,
20221
- emissiveIntensity: 0.4,
20222
- transparent: true,
20223
- opacity: TIMELINE_INTERVAL_BAR_OPACITY,
20224
- roughness: 0.3,
20225
- metalness: 0.8
20226
- });
20227
- const intervalBar = new THREE3.Mesh(barGeometry, barMaterial);
20336
+ allFinalPoints.push(new THREE3.Vector3(x, endY, 0));
20337
+ const intervalBar = createTimelineIntervalBracket();
20228
20338
  intervalBar.userData.associatedNodeId = mesh.userData.id;
20229
20339
  const endPosition = new THREE3.Vector3(x, endY, -0.5);
20230
20340
  intervalBar.userData.endPosition = endPosition;
20341
+ updateTimelineIntervalBracket(
20342
+ intervalBar,
20343
+ new THREE3.Vector3(x, y, 0),
20344
+ endPosition
20345
+ );
20231
20346
  timelineIntervalsGroup.add(intervalBar);
20232
20347
  const endLabelText = endDate.getFullYear().toString();
20233
20348
  const endLabel = createTextSprite(endLabelText, 48);
@@ -20240,7 +20355,21 @@ function XViewScene({
20240
20355
  }
20241
20356
  }
20242
20357
  });
20243
- }, []);
20358
+ if (allFinalPoints.length > 0) {
20359
+ const boundingBox = new THREE3.Box3().setFromPoints(allFinalPoints);
20360
+ const center = new THREE3.Vector3();
20361
+ boundingBox.getCenter(center);
20362
+ const size = new THREE3.Vector3();
20363
+ boundingBox.getSize(size);
20364
+ const spread = Math.max(size.x, size.y, size.z, 1);
20365
+ const desiredDistance = Math.max(
20366
+ x_view_config.CAMERA_ZOOM_DISTANCE,
20367
+ spread * 0.75
20368
+ );
20369
+ const zoomFactor = x_view_config.CAMERA_ZOOM_DISTANCE / desiredDistance;
20370
+ tweenToTarget(center, zoomFactor);
20371
+ }
20372
+ }, [tweenToTarget]);
20244
20373
  const handleVersionTimeline = useCallback8(
20245
20374
  (sourceMesh, versionMeshes) => {
20246
20375
  const { tweenGroup, timelineIntervalsGroup } = stateRef.current;
@@ -20276,7 +20405,9 @@ function XViewScene({
20276
20405
  const month = parseInt(parts[1], 10) - 1;
20277
20406
  const year = parseInt(parts[2], 10);
20278
20407
  if (!isNaN(day) && !isNaN(month) && !isNaN(year)) {
20279
- return new Date(year, month, day);
20408
+ const d = /* @__PURE__ */ new Date();
20409
+ d.setFullYear(year, month, day);
20410
+ return d;
20280
20411
  }
20281
20412
  }
20282
20413
  return null;
@@ -20347,22 +20478,7 @@ function XViewScene({
20347
20478
  if (type === "interval") {
20348
20479
  const relativeEndY = timeToYMap.get(endDate.getTime()) - timeToYMap.get(startDate.getTime());
20349
20480
  if (relativeEndY > 0) {
20350
- const barGeometry = new THREE3.CylinderGeometry(
20351
- TIMELINE_INTERVAL_BAR_RADIUS,
20352
- TIMELINE_INTERVAL_BAR_RADIUS,
20353
- 1,
20354
- 16
20355
- );
20356
- const barMaterial = new THREE3.MeshStandardMaterial({
20357
- color: TIMELINE_GOLD_COLOR,
20358
- emissive: 12092939,
20359
- emissiveIntensity: 0.4,
20360
- transparent: true,
20361
- opacity: TIMELINE_INTERVAL_BAR_OPACITY,
20362
- roughness: 0.3,
20363
- metalness: 0.8
20364
- });
20365
- const intervalBar = new THREE3.Mesh(barGeometry, barMaterial);
20481
+ const intervalBar = createTimelineIntervalBracket();
20366
20482
  intervalBar.userData.associatedNodeId = mesh.userData.id;
20367
20483
  const endPosition = new THREE3.Vector3(
20368
20484
  baseX,
@@ -20370,6 +20486,11 @@ function XViewScene({
20370
20486
  -0.5
20371
20487
  );
20372
20488
  intervalBar.userData.endPosition = endPosition;
20489
+ updateTimelineIntervalBracket(
20490
+ intervalBar,
20491
+ new THREE3.Vector3(x, y, 0),
20492
+ endPosition
20493
+ );
20373
20494
  timelineIntervalsGroup.add(intervalBar);
20374
20495
  const endLabelText = endDate.getFullYear().toString();
20375
20496
  const endLabel = createTextSprite(endLabelText, 48);
@@ -20865,25 +20986,15 @@ function XViewScene({
20865
20986
  const endY = timeToYMap.get(timeInfo.endDate.getTime());
20866
20987
  if (Math.abs(endY - targetY) > 0.1) {
20867
20988
  const barHeight = Math.abs(endY - targetY);
20868
- const barGeometry = new THREE3.CylinderGeometry(
20869
- TIMELINE_INTERVAL_BAR_RADIUS,
20870
- TIMELINE_INTERVAL_BAR_RADIUS,
20871
- 1,
20872
- 16
20873
- );
20874
- const barMaterial = new THREE3.MeshStandardMaterial({
20875
- color: TIMELINE_GOLD_COLOR,
20876
- emissive: 12092939,
20877
- emissiveIntensity: 0.4,
20878
- transparent: true,
20879
- opacity: TIMELINE_INTERVAL_BAR_OPACITY,
20880
- roughness: 0.3,
20881
- metalness: 0.8
20882
- });
20883
- const intervalBar = new THREE3.Mesh(barGeometry, barMaterial);
20989
+ const intervalBar = createTimelineIntervalBracket();
20884
20990
  intervalBar.userData.associatedNodeId = mesh.userData.id;
20885
- intervalBar.position.set(finalX, (targetY + endY) / 2, -0.5);
20886
- intervalBar.scale.y = barHeight;
20991
+ const endPosition = new THREE3.Vector3(finalX, endY, -0.5);
20992
+ intervalBar.userData.endPosition = endPosition;
20993
+ updateTimelineIntervalBracket(
20994
+ intervalBar,
20995
+ new THREE3.Vector3(finalX, targetY, 0),
20996
+ endPosition
20997
+ );
20887
20998
  graphGroup.add(intervalBar);
20888
20999
  mesh.userData.timelineIntervalBar = intervalBar;
20889
21000
  const endLabel = createTextSprite(
@@ -21652,23 +21763,12 @@ function XViewScene({
21652
21763
  }
21653
21764
  });
21654
21765
  (_a2 = stateRef.current.timelineIntervalsGroup) == null ? void 0 : _a2.children.forEach((obj) => {
21655
- if (obj.isMesh && obj.userData.associatedNodeId && obj.userData.endPosition) {
21766
+ if (obj.userData.associatedNodeId && obj.userData.endPosition) {
21656
21767
  const bar = obj;
21657
21768
  const node = stateRef.current.nodeObjects[String(bar.userData.associatedNodeId)];
21658
21769
  const endPos = bar.userData.endPosition;
21659
21770
  if (node) {
21660
- const startPos = node.position;
21661
- const distance = startPos.distanceTo(endPos);
21662
- bar.scale.y = distance;
21663
- const midpoint = new THREE3.Vector3().lerpVectors(
21664
- startPos,
21665
- endPos,
21666
- 0.5
21667
- );
21668
- bar.position.copy(midpoint);
21669
- const direction = new THREE3.Vector3().subVectors(endPos, startPos).normalize();
21670
- const upVector = new THREE3.Vector3(0, 1, 0);
21671
- bar.quaternion.setFromUnitVectors(upVector, direction);
21771
+ updateTimelineIntervalBracket(bar, node.position, endPos);
21672
21772
  }
21673
21773
  }
21674
21774
  });
@@ -24650,8 +24750,9 @@ function XViewScene({
24650
24750
  )) == null ? void 0 : _b.parentFileId : null;
24651
24751
  const detailsNodeDatasetInfo = detailsNode ? stateRef.current.nodeIdToParentFileMap.get(String(detailsNode.id)) : null;
24652
24752
  useEffect27(() => {
24653
- if (isInitialized && focusNodeId && !hasFocusedInitial) {
24654
- const nodeObjects = stateRef.current.nodeObjects || {};
24753
+ if (!isInitialized || hasFocusedInitial) return;
24754
+ const nodeObjects = stateRef.current.nodeObjects || {};
24755
+ if (focusNodeId) {
24655
24756
  const targetMesh = nodeObjects[String(focusNodeId)];
24656
24757
  if (targetMesh) {
24657
24758
  setTimeout(() => {
@@ -24664,11 +24765,34 @@ function XViewScene({
24664
24765
  "O link aponta para um item que n\xE3o foi encontrado ou foi exclu\xEDdo."
24665
24766
  );
24666
24767
  }
24768
+ } else if (!focusAncestryId) {
24769
+ const meshes = Object.values(nodeObjects);
24770
+ if (meshes.length > 0) {
24771
+ setTimeout(() => {
24772
+ const nodePositions = meshes.map((mesh) => mesh.position.clone());
24773
+ const boundingBox = new THREE3.Box3().setFromPoints(nodePositions);
24774
+ const center = new THREE3.Vector3();
24775
+ boundingBox.getCenter(center);
24776
+ const size = new THREE3.Vector3();
24777
+ boundingBox.getSize(size);
24778
+ const maxDim = Math.max(size.x, size.y, size.z);
24779
+ const desiredDistance = Math.max(
24780
+ x_view_config.CAMERA_ZOOM_DISTANCE,
24781
+ maxDim * 1.8
24782
+ );
24783
+ const zoomFactor = x_view_config.CAMERA_ZOOM_DISTANCE / desiredDistance;
24784
+ tweenToTarget(center, zoomFactor);
24785
+ setHasFocusedInitial(true);
24786
+ }, 300);
24787
+ } else {
24788
+ setHasFocusedInitial(true);
24789
+ }
24667
24790
  }
24668
24791
  }, [
24669
24792
  isInitialized,
24670
24793
  sceneVersion,
24671
24794
  focusNodeId,
24795
+ focusAncestryId,
24672
24796
  hasFocusedInitial,
24673
24797
  tweenToTarget
24674
24798
  ]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lv-x-software-house/x_view",
3
- "version": "1.3.5",
3
+ "version": "1.3.6",
4
4
  "description": "Pacote privado contendo os componentes e lógica de renderização 3D do X View.",
5
5
  "author": "iv.x - Engenharia de Software - ivxsoftwarehouse@gmail.com",
6
6
  "license": "UNLICENSED",