@lv-x-software-house/x_view 1.2.5-dev.43 → 1.2.5-dev.45

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 +219 -89
  2. package/dist/index.mjs +219 -89
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1334,6 +1334,15 @@ var createNodeMesh = (nodeData, position, glowTexture) => {
1334
1334
  label.name = "label";
1335
1335
  mesh.userData.labelObject = label;
1336
1336
  mesh.userData.labelOffset = new THREE.Vector3(0, 3.8, 0);
1337
+ if (nodeData.is_quest && nodeData.Date) {
1338
+ const timeInfo = parseDateProp(nodeData.Date);
1339
+ if (!timeInfo.isUndated && timeInfo.label) {
1340
+ const dateSprite = createTextSprite(timeInfo.label, 36);
1341
+ dateSprite.name = "questDateLabel";
1342
+ dateSprite.position.set(0, -5.5, 0);
1343
+ mesh.add(dateSprite);
1344
+ }
1345
+ }
1337
1346
  return mesh;
1338
1347
  };
1339
1348
  var updateExistingNodeVisuals = (state, updatedNode) => {
@@ -3321,9 +3330,14 @@ var userActionHandlers = {
3321
3330
  };
3322
3331
  specificParentData.nodes.push(nextQuestNode);
3323
3332
  if (stateRef.current.nodeObjects[String(nodeToSave.id)]) {
3324
- const pos = stateRef.current.nodeObjects[String(nodeToSave.id)].position.clone();
3325
- pos.x += 12;
3326
- addStandaloneNodeToScene(stateRef.current, nextQuestNode, pos);
3333
+ const oldMeshPosition = stateRef.current.nodeObjects[String(nodeToSave.id)].position.clone();
3334
+ setTimeout(() => {
3335
+ if (stateRef.current && stateRef.current.graphGroup) {
3336
+ const pos = oldMeshPosition.clone();
3337
+ pos.x += 12;
3338
+ addStandaloneNodeToScene(stateRef.current, nextQuestNode, pos);
3339
+ }
3340
+ }, 2e3);
3327
3341
  }
3328
3342
  }
3329
3343
  }
@@ -4266,6 +4280,78 @@ function calculateNextRecurrenceDate(config) {
4266
4280
  confirmed: true
4267
4281
  };
4268
4282
  }
4283
+ var parseDate = (str) => {
4284
+ if (!str) return null;
4285
+ const parts = str.split("/");
4286
+ if (parts.length === 3) {
4287
+ const day = parseInt(parts[0], 10);
4288
+ const month = parseInt(parts[1], 10) - 1;
4289
+ const year = parseInt(parts[2], 10);
4290
+ if (!isNaN(day) && !isNaN(month) && !isNaN(year)) {
4291
+ return new Date(year, month, day);
4292
+ }
4293
+ }
4294
+ return null;
4295
+ };
4296
+ var parseDateProp = (dateProp) => {
4297
+ let info = { isUndated: true, label: "", type: "point" };
4298
+ if (!dateProp) return info;
4299
+ if (dateProp.type === "Data") {
4300
+ const d = parseDate(dateProp.value);
4301
+ if (d) {
4302
+ info = { startDate: d, endDate: d, label: dateProp.value, type: "point", isUndated: false };
4303
+ }
4304
+ } else if (dateProp.type === "Date Interval" || dateProp.type === "Date Range" || dateProp.type === "interval") {
4305
+ const sd = parseDate(dateProp.start || dateProp.value);
4306
+ const ed = parseDate(dateProp.end);
4307
+ if (sd) {
4308
+ info = {
4309
+ startDate: sd,
4310
+ endDate: ed || sd,
4311
+ label: `${dateProp.start || dateProp.value}${ed ? " - " + dateProp.end : ""}`,
4312
+ type: ed ? "interval" : "point",
4313
+ isUndated: false
4314
+ };
4315
+ }
4316
+ } else if (dateProp.date) {
4317
+ const d = parseDate(dateProp.date);
4318
+ if (d)
4319
+ info = {
4320
+ startDate: d,
4321
+ endDate: d,
4322
+ label: dateProp.date,
4323
+ type: "point",
4324
+ isUndated: false
4325
+ };
4326
+ } else if (dateProp.year) {
4327
+ const y = parseInt(dateProp.year, 10);
4328
+ if (!isNaN(y)) {
4329
+ const d = new Date(y, 0, 1);
4330
+ info = {
4331
+ startDate: d,
4332
+ endDate: d,
4333
+ label: String(y),
4334
+ type: "point",
4335
+ isUndated: false
4336
+ };
4337
+ }
4338
+ } else if (dateProp.date_interval && dateProp.date_interval.start) {
4339
+ const sY = parseInt(dateProp.date_interval.start, 10);
4340
+ const eY = parseInt(dateProp.date_interval.end, 10) || sY;
4341
+ if (!isNaN(sY)) {
4342
+ const sd = new Date(sY, 0, 1);
4343
+ const ed = new Date(eY, 11, 31);
4344
+ info = {
4345
+ startDate: sd,
4346
+ endDate: ed,
4347
+ label: `${sY} - ${eY}`,
4348
+ type: "interval",
4349
+ isUndated: false
4350
+ };
4351
+ }
4352
+ }
4353
+ return info;
4354
+ };
4269
4355
 
4270
4356
  // src/components/CustomPropertyDisplay.jsx
4271
4357
  var import_react4 = __toESM(require("react"));
@@ -9472,7 +9558,7 @@ function InSceneQuestForm({
9472
9558
  textureImageUrl: null,
9473
9559
  reviewStack,
9474
9560
  timeline: initialTimeline,
9475
- Date: questDate,
9561
+ Date: questDate ? { ...questDate, confirmed: true } : null,
9476
9562
  recurrence_config: recurrenceConfig,
9477
9563
  ...additionalData
9478
9564
  });
@@ -10936,7 +11022,7 @@ ${noteText}` : noteText : entry.notes,
10936
11022
  description_sections: processedSections,
10937
11023
  intensity,
10938
11024
  recurrence_config: currentRecurrenceConfig,
10939
- Date: questDate,
11025
+ Date: questDate ? { ...questDate, confirmed: true } : null,
10940
11026
  useImageAsTexture: false,
10941
11027
  textureImageUrl: null,
10942
11028
  is_quest: true,
@@ -13499,7 +13585,7 @@ function XViewScene({
13499
13585
  }
13500
13586
  }
13501
13587
  Object.values(nodeObjects).forEach((mesh) => {
13502
- const oldLabel = mesh.getObjectByName("timelineLabel") || mesh.getObjectByName("questDateLabel");
13588
+ const oldLabel = mesh.getObjectByName("timelineLabel");
13503
13589
  if (oldLabel) {
13504
13590
  mesh.remove(oldLabel);
13505
13591
  if (oldLabel.material.map) oldLabel.material.map.dispose();
@@ -13510,7 +13596,7 @@ function XViewScene({
13510
13596
  });
13511
13597
  const timelineNodes = [];
13512
13598
  const allTimePoints = /* @__PURE__ */ new Set();
13513
- const parseDate = (str) => {
13599
+ const parseDate2 = (str) => {
13514
13600
  const parts = str.split("/");
13515
13601
  if (parts.length === 3) {
13516
13602
  const day = parseInt(parts[0], 10);
@@ -13527,7 +13613,7 @@ function XViewScene({
13527
13613
  let nodeInfo = { mesh, type: "point", label: "", isUndated: true };
13528
13614
  if (dateProp) {
13529
13615
  if (dateProp.date) {
13530
- const date = parseDate(dateProp.date);
13616
+ const date = parseDate2(dateProp.date);
13531
13617
  if (date) {
13532
13618
  nodeInfo.startDate = date;
13533
13619
  nodeInfo.endDate = date;
@@ -13673,7 +13759,7 @@ function XViewScene({
13673
13759
  });
13674
13760
  const timelineNodes = [];
13675
13761
  const allTimePoints = /* @__PURE__ */ new Set();
13676
- const parseDate = (str) => {
13762
+ const parseDate2 = (str) => {
13677
13763
  const parts = str.split("/");
13678
13764
  if (parts.length === 3) {
13679
13765
  const day = parseInt(parts[0], 10);
@@ -13690,7 +13776,7 @@ function XViewScene({
13690
13776
  if (!dateProp) return;
13691
13777
  let nodeInfo = { mesh, type: "point", label: "" };
13692
13778
  if (dateProp.date) {
13693
- const date = parseDate(dateProp.date);
13779
+ const date = parseDate2(dateProp.date);
13694
13780
  if (date) {
13695
13781
  nodeInfo.startDate = date;
13696
13782
  nodeInfo.endDate = date;
@@ -13932,16 +14018,8 @@ function XViewScene({
13932
14018
  );
13933
14019
  const handleActivateQuestLens = (0, import_react28.useCallback)(({ assigneeId, status: status2 }) => {
13934
14020
  var _a2;
13935
- const { nodeObjects, tweenGroup, graphGroup, timelineIntervalsGroup } = stateRef.current;
14021
+ const { nodeObjects, tweenGroup, graphGroup } = stateRef.current;
13936
14022
  if (!nodeObjects || !tweenGroup || !graphGroup) return;
13937
- Object.values(nodeObjects).forEach((mesh) => {
13938
- const oldLabel = mesh.getObjectByName("questDateLabel") || mesh.getObjectByName("timelineLabel");
13939
- if (oldLabel) {
13940
- mesh.remove(oldLabel);
13941
- if (oldLabel.material.map) oldLabel.material.map.dispose();
13942
- oldLabel.material.dispose();
13943
- }
13944
- });
13945
14023
  const allQuests = ((_a2 = sceneDataRef.current) == null ? void 0 : _a2.quest_nodes) || [];
13946
14024
  const filteredQuests = allQuests.filter((q) => {
13947
14025
  const matchAssignee = !assigneeId || String(q.assignee_id) === String(assigneeId);
@@ -13963,76 +14041,91 @@ function XViewScene({
13963
14041
  questsByStatus[q.status].push(q);
13964
14042
  }
13965
14043
  });
13966
- const parseDate = (str) => {
13967
- if (!str) return null;
13968
- const parts = str.split("/");
13969
- if (parts.length === 3) {
13970
- const day = parseInt(parts[0], 10);
13971
- const month = parseInt(parts[1], 10) - 1;
13972
- const year = parseInt(parts[2], 10);
13973
- if (!isNaN(day) && !isNaN(month) && !isNaN(year)) {
13974
- return new Date(year, month, day);
13975
- }
13976
- }
13977
- return null;
13978
- };
13979
- const timePoints = /* @__PURE__ */ new Set();
13980
14044
  const questTimeInfos = /* @__PURE__ */ new Map();
14045
+ const allActiveTimePoints = /* @__PURE__ */ new Set();
13981
14046
  filteredQuests.forEach((q) => {
13982
- const dateProp = q.Date;
13983
- let info = { isUndated: true, label: "" };
13984
- if (dateProp) {
13985
- if (dateProp.date) {
13986
- const d = parseDate(dateProp.date);
13987
- if (d) {
13988
- info = { startDate: d, endDate: d, label: dateProp.date, isUndated: false };
13989
- timePoints.add(d.getTime());
13990
- }
13991
- } else if (dateProp.year) {
13992
- const y = parseInt(dateProp.year, 10);
13993
- if (!isNaN(y)) {
13994
- const d = new Date(y, 0, 1);
13995
- info = { startDate: d, endDate: d, label: String(y), isUndated: false };
13996
- timePoints.add(d.getTime());
13997
- }
13998
- } else if (dateProp.date_interval && dateProp.date_interval.start) {
13999
- const sY = parseInt(dateProp.date_interval.start, 10);
14000
- const eY = parseInt(dateProp.date_interval.end, 10) || sY;
14001
- if (!isNaN(sY)) {
14002
- const sd = new Date(sY, 0, 1);
14003
- const ed = new Date(eY, 11, 31);
14004
- info = { startDate: sd, endDate: ed, label: `${sY}-${eY}`, isUndated: false };
14005
- timePoints.add(sd.getTime());
14006
- timePoints.add(ed.getTime());
14007
- }
14047
+ const info = parseDateProp(q.Date);
14048
+ questTimeInfos.set(String(q.id), info);
14049
+ if (!info.isUndated) {
14050
+ allActiveTimePoints.add(info.startDate.getTime());
14051
+ if (info.endDate && info.type === "interval") {
14052
+ allActiveTimePoints.add(info.endDate.getTime());
14008
14053
  }
14009
14054
  }
14010
- questTimeInfos.set(String(q.id), info);
14011
14055
  });
14012
- const sortedTimePoints = Array.from(timePoints).sort((a, b) => a - b);
14056
+ const sortedTimePoints = Array.from(allActiveTimePoints).sort((a, b) => a - b);
14013
14057
  const timeToYMap = /* @__PURE__ */ new Map();
14014
- if (sortedTimePoints.length > 0) {
14015
- const maxIdx = sortedTimePoints.length - 1;
14016
- sortedTimePoints.forEach((t, i) => {
14017
- timeToYMap.set(t, (i - maxIdx) * (QUEST_LENS_ROW_SPACING || 30));
14018
- });
14019
- }
14058
+ sortedTimePoints.forEach((ms, idx) => {
14059
+ timeToYMap.set(ms, idx * TIMELINE_LAYER_SPACING_Y);
14060
+ });
14020
14061
  const startX = -((QUEST_STATUS_ORDER.length - 1) * QUEST_LENS_COLUMN_SPACING) / 2;
14021
- Object.keys(nodeObjects).forEach((id) => {
14022
- const mesh = nodeObjects[id];
14023
- const isFilteredQuest = filteredQuests.some(
14024
- (q) => String(q.id) === String(id)
14025
- );
14026
- if (!isFilteredQuest) {
14027
- if (mesh.material) {
14028
- const fadeTween = new import_tween2.Tween(mesh.material).to({ opacity: 0.15 }, 1200).easing(import_tween2.Easing.Cubic.Out);
14029
- tweenGroup.add(fadeTween);
14030
- fadeTween.start();
14031
- }
14032
- if (mesh.userData.labelObject) {
14033
- mesh.userData.labelObject.visible = false;
14062
+ const positionGroups = /* @__PURE__ */ new Map();
14063
+ const NODE_X_GAP = 22;
14064
+ QUEST_STATUS_ORDER.forEach((statusName) => {
14065
+ const questsInCol = questsByStatus[statusName];
14066
+ questsInCol.forEach((q, rowIndex) => {
14067
+ const timeInfo = questTimeInfos.get(String(q.id)) || { isUndated: true };
14068
+ let nodeY;
14069
+ if (!timeInfo.isUndated) {
14070
+ nodeY = timeToYMap.get(timeInfo.startDate.getTime());
14071
+ } else {
14072
+ const undatedOffset = sortedTimePoints.length > 0 ? sortedTimePoints.length * 5 : 0;
14073
+ nodeY = -40 - rowIndex * QUEST_LENS_ROW_SPACING - undatedOffset;
14034
14074
  }
14035
- } else {
14075
+ const posKey = `${statusName}_${nodeY}`;
14076
+ if (!positionGroups.has(posKey)) positionGroups.set(posKey, []);
14077
+ positionGroups.get(posKey).push(String(q.id));
14078
+ });
14079
+ });
14080
+ Object.values(nodeObjects).forEach((mesh) => {
14081
+ const oldLabel = mesh.getObjectByName("questDateLabel") || mesh.getObjectByName("timelineLabel");
14082
+ if (oldLabel) {
14083
+ mesh.remove(oldLabel);
14084
+ if (oldLabel.material) oldLabel.material.dispose();
14085
+ }
14086
+ if (mesh.userData.timelineIntervalBar) {
14087
+ if (graphGroup) graphGroup.remove(mesh.userData.timelineIntervalBar);
14088
+ mesh.userData.timelineIntervalBar = null;
14089
+ }
14090
+ if (mesh.userData.timelineEndLabel) {
14091
+ if (graphGroup) graphGroup.remove(mesh.userData.timelineEndLabel);
14092
+ mesh.userData.timelineEndLabel = null;
14093
+ }
14094
+ });
14095
+ const nonFilteredNodeIds = Object.keys(nodeObjects).filter((id) => !filteredQuests.some((q) => String(q.id) === String(id)));
14096
+ const COMMON_START_X = startX - 100;
14097
+ const COMMON_COL_SPACING = 30;
14098
+ const COMMON_ROW_SPACING = 35;
14099
+ const MAX_COMMON_ROWS = 15;
14100
+ nonFilteredNodeIds.forEach((id, idx) => {
14101
+ const mesh = nodeObjects[id];
14102
+ if (!mesh) return;
14103
+ const col = Math.floor(idx / MAX_COMMON_ROWS);
14104
+ const row = idx % MAX_COMMON_ROWS;
14105
+ const targetX = COMMON_START_X - col * COMMON_COL_SPACING;
14106
+ const targetY = -(row * COMMON_ROW_SPACING) + 50;
14107
+ const targetZ = 0;
14108
+ if (mesh.material) {
14109
+ const opacityTween = new import_tween2.Tween(mesh.material).to({ opacity: 1 }, 1200).easing(import_tween2.Easing.Cubic.Out);
14110
+ tweenGroup.add(opacityTween);
14111
+ opacityTween.start();
14112
+ }
14113
+ if (mesh.userData.labelObject) {
14114
+ mesh.userData.labelObject.visible = true;
14115
+ const labelTargetPos = new THREE3.Vector3(targetX, targetY, targetZ).add(
14116
+ mesh.userData.labelOffset || new THREE3.Vector3(0, 3.8, 0)
14117
+ );
14118
+ const labelTween = new import_tween2.Tween(mesh.userData.labelObject.position).to(labelTargetPos, 1500).easing(import_tween2.Easing.Cubic.InOut);
14119
+ tweenGroup.add(labelTween);
14120
+ labelTween.start();
14121
+ }
14122
+ const sideTween = new import_tween2.Tween(mesh.position).to({ x: targetX, y: targetY, z: targetZ }, 1500).easing(import_tween2.Easing.Cubic.InOut);
14123
+ tweenGroup.add(sideTween);
14124
+ sideTween.start();
14125
+ });
14126
+ filteredQuests.forEach((q) => {
14127
+ const mesh = nodeObjects[String(q.id)];
14128
+ if (mesh) {
14036
14129
  if (mesh.material) {
14037
14130
  const fadeInTween = new import_tween2.Tween(mesh.material).to({ opacity: 1 }, 1200).easing(import_tween2.Easing.Cubic.Out);
14038
14131
  tweenGroup.add(fadeInTween);
@@ -14050,29 +14143,66 @@ function XViewScene({
14050
14143
  questsInCol.forEach((q, rowIndex) => {
14051
14144
  const nodeId = String(q.id);
14052
14145
  let mesh = nodeObjects[nodeId];
14053
- if (!mesh) {
14054
- mesh = addOrUpdateNodeMesh(q, new THREE3.Vector3(colX, 150, 0), true);
14055
- }
14056
14146
  const timeInfo = questTimeInfos.get(nodeId) || { isUndated: true };
14057
14147
  let targetY;
14058
14148
  if (!timeInfo.isUndated) {
14059
14149
  targetY = timeToYMap.get(timeInfo.startDate.getTime());
14060
14150
  } else {
14061
14151
  const undatedOffset = sortedTimePoints.length > 0 ? sortedTimePoints.length * 5 : 0;
14062
- targetY = -80 - rowIndex * (QUEST_LENS_ROW_SPACING || 30) - undatedOffset;
14152
+ targetY = -40 - rowIndex * QUEST_LENS_ROW_SPACING - undatedOffset;
14063
14153
  }
14064
- const lensTween = new import_tween2.Tween(mesh.position).to({ x: colX, y: targetY, z: 0 }, 1500).easing(import_tween2.Easing.Cubic.InOut);
14154
+ const posKey = `${statusName}_${targetY}`;
14155
+ const group = positionGroups.get(posKey) || [nodeId];
14156
+ const indexInGroup = group.indexOf(nodeId);
14157
+ const xOffset = (indexInGroup - (group.length - 1) / 2) * NODE_X_GAP;
14158
+ const finalX = colX + xOffset;
14159
+ if (!mesh) {
14160
+ mesh = addOrUpdateNodeMesh(q, new THREE3.Vector3(finalX, 150, 0), true);
14161
+ }
14162
+ const lensTween = new import_tween2.Tween(mesh.position).to({ x: finalX, y: targetY, z: 0 }, 1500).easing(import_tween2.Easing.Cubic.InOut);
14065
14163
  tweenGroup.add(lensTween);
14066
14164
  lensTween.start();
14067
- if (!timeInfo.isUndated && timeInfo.label) {
14165
+ if (!timeInfo.isUndated && timeInfo.label && !mesh.getObjectByName("questDateLabel")) {
14068
14166
  const dateSprite = createTextSprite(timeInfo.label, 36);
14069
14167
  dateSprite.name = "questDateLabel";
14070
14168
  dateSprite.position.set(0, -5.5, 0);
14071
14169
  mesh.add(dateSprite);
14072
14170
  }
14171
+ if (timeInfo.type === "interval" && timeToYMap.has(timeInfo.endDate.getTime())) {
14172
+ const endY = timeToYMap.get(timeInfo.endDate.getTime());
14173
+ if (Math.abs(endY - targetY) > 0.1) {
14174
+ const barHeight = Math.abs(endY - targetY);
14175
+ const barGeometry = new THREE3.CylinderGeometry(
14176
+ TIMELINE_INTERVAL_BAR_RADIUS,
14177
+ TIMELINE_INTERVAL_BAR_RADIUS,
14178
+ 1,
14179
+ 16
14180
+ );
14181
+ const barMaterial = new THREE3.MeshStandardMaterial({
14182
+ color: TIMELINE_GOLD_COLOR,
14183
+ emissive: 12092939,
14184
+ emissiveIntensity: 0.4,
14185
+ transparent: true,
14186
+ opacity: TIMELINE_INTERVAL_BAR_OPACITY,
14187
+ roughness: 0.3,
14188
+ metalness: 0.8
14189
+ });
14190
+ const intervalBar = new THREE3.Mesh(barGeometry, barMaterial);
14191
+ intervalBar.userData.associatedNodeId = mesh.userData.id;
14192
+ intervalBar.position.set(finalX, (targetY + endY) / 2, -0.5);
14193
+ intervalBar.scale.y = barHeight;
14194
+ graphGroup.add(intervalBar);
14195
+ mesh.userData.timelineIntervalBar = intervalBar;
14196
+ const endLabel = createTextSprite(timeInfo.label.split(" - ")[1] || "", 30);
14197
+ endLabel.name = "timelineEndLabel";
14198
+ endLabel.position.set(finalX, endY + 4, 0);
14199
+ graphGroup.add(endLabel);
14200
+ mesh.userData.timelineEndLabel = endLabel;
14201
+ }
14202
+ }
14073
14203
  if (mesh.userData.labelObject) {
14074
- const labelTargetPos = new THREE3.Vector3(colX, targetY, 0).add(
14075
- mesh.userData.labelOffset || new THREE3.Vector3(0, -2.5, 0)
14204
+ const labelTargetPos = new THREE3.Vector3(finalX, targetY, 0).add(
14205
+ mesh.userData.labelOffset || new THREE3.Vector3(0, 3.8, 0)
14076
14206
  );
14077
14207
  const labelTween = new import_tween2.Tween(mesh.userData.labelObject.position).to(labelTargetPos, 1500).easing(import_tween2.Easing.Cubic.InOut);
14078
14208
  tweenGroup.add(labelTween);
package/dist/index.mjs CHANGED
@@ -1295,6 +1295,15 @@ var createNodeMesh = (nodeData, position, glowTexture) => {
1295
1295
  label.name = "label";
1296
1296
  mesh.userData.labelObject = label;
1297
1297
  mesh.userData.labelOffset = new THREE.Vector3(0, 3.8, 0);
1298
+ if (nodeData.is_quest && nodeData.Date) {
1299
+ const timeInfo = parseDateProp(nodeData.Date);
1300
+ if (!timeInfo.isUndated && timeInfo.label) {
1301
+ const dateSprite = createTextSprite(timeInfo.label, 36);
1302
+ dateSprite.name = "questDateLabel";
1303
+ dateSprite.position.set(0, -5.5, 0);
1304
+ mesh.add(dateSprite);
1305
+ }
1306
+ }
1298
1307
  return mesh;
1299
1308
  };
1300
1309
  var updateExistingNodeVisuals = (state, updatedNode) => {
@@ -3282,9 +3291,14 @@ var userActionHandlers = {
3282
3291
  };
3283
3292
  specificParentData.nodes.push(nextQuestNode);
3284
3293
  if (stateRef.current.nodeObjects[String(nodeToSave.id)]) {
3285
- const pos = stateRef.current.nodeObjects[String(nodeToSave.id)].position.clone();
3286
- pos.x += 12;
3287
- addStandaloneNodeToScene(stateRef.current, nextQuestNode, pos);
3294
+ const oldMeshPosition = stateRef.current.nodeObjects[String(nodeToSave.id)].position.clone();
3295
+ setTimeout(() => {
3296
+ if (stateRef.current && stateRef.current.graphGroup) {
3297
+ const pos = oldMeshPosition.clone();
3298
+ pos.x += 12;
3299
+ addStandaloneNodeToScene(stateRef.current, nextQuestNode, pos);
3300
+ }
3301
+ }, 2e3);
3288
3302
  }
3289
3303
  }
3290
3304
  }
@@ -4227,6 +4241,78 @@ function calculateNextRecurrenceDate(config) {
4227
4241
  confirmed: true
4228
4242
  };
4229
4243
  }
4244
+ var parseDate = (str) => {
4245
+ if (!str) return null;
4246
+ const parts = str.split("/");
4247
+ if (parts.length === 3) {
4248
+ const day = parseInt(parts[0], 10);
4249
+ const month = parseInt(parts[1], 10) - 1;
4250
+ const year = parseInt(parts[2], 10);
4251
+ if (!isNaN(day) && !isNaN(month) && !isNaN(year)) {
4252
+ return new Date(year, month, day);
4253
+ }
4254
+ }
4255
+ return null;
4256
+ };
4257
+ var parseDateProp = (dateProp) => {
4258
+ let info = { isUndated: true, label: "", type: "point" };
4259
+ if (!dateProp) return info;
4260
+ if (dateProp.type === "Data") {
4261
+ const d = parseDate(dateProp.value);
4262
+ if (d) {
4263
+ info = { startDate: d, endDate: d, label: dateProp.value, type: "point", isUndated: false };
4264
+ }
4265
+ } else if (dateProp.type === "Date Interval" || dateProp.type === "Date Range" || dateProp.type === "interval") {
4266
+ const sd = parseDate(dateProp.start || dateProp.value);
4267
+ const ed = parseDate(dateProp.end);
4268
+ if (sd) {
4269
+ info = {
4270
+ startDate: sd,
4271
+ endDate: ed || sd,
4272
+ label: `${dateProp.start || dateProp.value}${ed ? " - " + dateProp.end : ""}`,
4273
+ type: ed ? "interval" : "point",
4274
+ isUndated: false
4275
+ };
4276
+ }
4277
+ } else if (dateProp.date) {
4278
+ const d = parseDate(dateProp.date);
4279
+ if (d)
4280
+ info = {
4281
+ startDate: d,
4282
+ endDate: d,
4283
+ label: dateProp.date,
4284
+ type: "point",
4285
+ isUndated: false
4286
+ };
4287
+ } else if (dateProp.year) {
4288
+ const y = parseInt(dateProp.year, 10);
4289
+ if (!isNaN(y)) {
4290
+ const d = new Date(y, 0, 1);
4291
+ info = {
4292
+ startDate: d,
4293
+ endDate: d,
4294
+ label: String(y),
4295
+ type: "point",
4296
+ isUndated: false
4297
+ };
4298
+ }
4299
+ } else if (dateProp.date_interval && dateProp.date_interval.start) {
4300
+ const sY = parseInt(dateProp.date_interval.start, 10);
4301
+ const eY = parseInt(dateProp.date_interval.end, 10) || sY;
4302
+ if (!isNaN(sY)) {
4303
+ const sd = new Date(sY, 0, 1);
4304
+ const ed = new Date(eY, 11, 31);
4305
+ info = {
4306
+ startDate: sd,
4307
+ endDate: ed,
4308
+ label: `${sY} - ${eY}`,
4309
+ type: "interval",
4310
+ isUndated: false
4311
+ };
4312
+ }
4313
+ }
4314
+ return info;
4315
+ };
4230
4316
 
4231
4317
  // src/components/CustomPropertyDisplay.jsx
4232
4318
  import React3, { useState as useState4, useRef as useRef3, useEffect as useEffect3 } from "react";
@@ -9491,7 +9577,7 @@ function InSceneQuestForm({
9491
9577
  textureImageUrl: null,
9492
9578
  reviewStack,
9493
9579
  timeline: initialTimeline,
9494
- Date: questDate,
9580
+ Date: questDate ? { ...questDate, confirmed: true } : null,
9495
9581
  recurrence_config: recurrenceConfig,
9496
9582
  ...additionalData
9497
9583
  });
@@ -10967,7 +11053,7 @@ ${noteText}` : noteText : entry.notes,
10967
11053
  description_sections: processedSections,
10968
11054
  intensity,
10969
11055
  recurrence_config: currentRecurrenceConfig,
10970
- Date: questDate,
11056
+ Date: questDate ? { ...questDate, confirmed: true } : null,
10971
11057
  useImageAsTexture: false,
10972
11058
  textureImageUrl: null,
10973
11059
  is_quest: true,
@@ -13543,7 +13629,7 @@ function XViewScene({
13543
13629
  }
13544
13630
  }
13545
13631
  Object.values(nodeObjects).forEach((mesh) => {
13546
- const oldLabel = mesh.getObjectByName("timelineLabel") || mesh.getObjectByName("questDateLabel");
13632
+ const oldLabel = mesh.getObjectByName("timelineLabel");
13547
13633
  if (oldLabel) {
13548
13634
  mesh.remove(oldLabel);
13549
13635
  if (oldLabel.material.map) oldLabel.material.map.dispose();
@@ -13554,7 +13640,7 @@ function XViewScene({
13554
13640
  });
13555
13641
  const timelineNodes = [];
13556
13642
  const allTimePoints = /* @__PURE__ */ new Set();
13557
- const parseDate = (str) => {
13643
+ const parseDate2 = (str) => {
13558
13644
  const parts = str.split("/");
13559
13645
  if (parts.length === 3) {
13560
13646
  const day = parseInt(parts[0], 10);
@@ -13571,7 +13657,7 @@ function XViewScene({
13571
13657
  let nodeInfo = { mesh, type: "point", label: "", isUndated: true };
13572
13658
  if (dateProp) {
13573
13659
  if (dateProp.date) {
13574
- const date = parseDate(dateProp.date);
13660
+ const date = parseDate2(dateProp.date);
13575
13661
  if (date) {
13576
13662
  nodeInfo.startDate = date;
13577
13663
  nodeInfo.endDate = date;
@@ -13717,7 +13803,7 @@ function XViewScene({
13717
13803
  });
13718
13804
  const timelineNodes = [];
13719
13805
  const allTimePoints = /* @__PURE__ */ new Set();
13720
- const parseDate = (str) => {
13806
+ const parseDate2 = (str) => {
13721
13807
  const parts = str.split("/");
13722
13808
  if (parts.length === 3) {
13723
13809
  const day = parseInt(parts[0], 10);
@@ -13734,7 +13820,7 @@ function XViewScene({
13734
13820
  if (!dateProp) return;
13735
13821
  let nodeInfo = { mesh, type: "point", label: "" };
13736
13822
  if (dateProp.date) {
13737
- const date = parseDate(dateProp.date);
13823
+ const date = parseDate2(dateProp.date);
13738
13824
  if (date) {
13739
13825
  nodeInfo.startDate = date;
13740
13826
  nodeInfo.endDate = date;
@@ -13976,16 +14062,8 @@ function XViewScene({
13976
14062
  );
13977
14063
  const handleActivateQuestLens = useCallback4(({ assigneeId, status: status2 }) => {
13978
14064
  var _a2;
13979
- const { nodeObjects, tweenGroup, graphGroup, timelineIntervalsGroup } = stateRef.current;
14065
+ const { nodeObjects, tweenGroup, graphGroup } = stateRef.current;
13980
14066
  if (!nodeObjects || !tweenGroup || !graphGroup) return;
13981
- Object.values(nodeObjects).forEach((mesh) => {
13982
- const oldLabel = mesh.getObjectByName("questDateLabel") || mesh.getObjectByName("timelineLabel");
13983
- if (oldLabel) {
13984
- mesh.remove(oldLabel);
13985
- if (oldLabel.material.map) oldLabel.material.map.dispose();
13986
- oldLabel.material.dispose();
13987
- }
13988
- });
13989
14067
  const allQuests = ((_a2 = sceneDataRef.current) == null ? void 0 : _a2.quest_nodes) || [];
13990
14068
  const filteredQuests = allQuests.filter((q) => {
13991
14069
  const matchAssignee = !assigneeId || String(q.assignee_id) === String(assigneeId);
@@ -14007,76 +14085,91 @@ function XViewScene({
14007
14085
  questsByStatus[q.status].push(q);
14008
14086
  }
14009
14087
  });
14010
- const parseDate = (str) => {
14011
- if (!str) return null;
14012
- const parts = str.split("/");
14013
- if (parts.length === 3) {
14014
- const day = parseInt(parts[0], 10);
14015
- const month = parseInt(parts[1], 10) - 1;
14016
- const year = parseInt(parts[2], 10);
14017
- if (!isNaN(day) && !isNaN(month) && !isNaN(year)) {
14018
- return new Date(year, month, day);
14019
- }
14020
- }
14021
- return null;
14022
- };
14023
- const timePoints = /* @__PURE__ */ new Set();
14024
14088
  const questTimeInfos = /* @__PURE__ */ new Map();
14089
+ const allActiveTimePoints = /* @__PURE__ */ new Set();
14025
14090
  filteredQuests.forEach((q) => {
14026
- const dateProp = q.Date;
14027
- let info = { isUndated: true, label: "" };
14028
- if (dateProp) {
14029
- if (dateProp.date) {
14030
- const d = parseDate(dateProp.date);
14031
- if (d) {
14032
- info = { startDate: d, endDate: d, label: dateProp.date, isUndated: false };
14033
- timePoints.add(d.getTime());
14034
- }
14035
- } else if (dateProp.year) {
14036
- const y = parseInt(dateProp.year, 10);
14037
- if (!isNaN(y)) {
14038
- const d = new Date(y, 0, 1);
14039
- info = { startDate: d, endDate: d, label: String(y), isUndated: false };
14040
- timePoints.add(d.getTime());
14041
- }
14042
- } else if (dateProp.date_interval && dateProp.date_interval.start) {
14043
- const sY = parseInt(dateProp.date_interval.start, 10);
14044
- const eY = parseInt(dateProp.date_interval.end, 10) || sY;
14045
- if (!isNaN(sY)) {
14046
- const sd = new Date(sY, 0, 1);
14047
- const ed = new Date(eY, 11, 31);
14048
- info = { startDate: sd, endDate: ed, label: `${sY}-${eY}`, isUndated: false };
14049
- timePoints.add(sd.getTime());
14050
- timePoints.add(ed.getTime());
14051
- }
14091
+ const info = parseDateProp(q.Date);
14092
+ questTimeInfos.set(String(q.id), info);
14093
+ if (!info.isUndated) {
14094
+ allActiveTimePoints.add(info.startDate.getTime());
14095
+ if (info.endDate && info.type === "interval") {
14096
+ allActiveTimePoints.add(info.endDate.getTime());
14052
14097
  }
14053
14098
  }
14054
- questTimeInfos.set(String(q.id), info);
14055
14099
  });
14056
- const sortedTimePoints = Array.from(timePoints).sort((a, b) => a - b);
14100
+ const sortedTimePoints = Array.from(allActiveTimePoints).sort((a, b) => a - b);
14057
14101
  const timeToYMap = /* @__PURE__ */ new Map();
14058
- if (sortedTimePoints.length > 0) {
14059
- const maxIdx = sortedTimePoints.length - 1;
14060
- sortedTimePoints.forEach((t, i) => {
14061
- timeToYMap.set(t, (i - maxIdx) * (QUEST_LENS_ROW_SPACING || 30));
14062
- });
14063
- }
14102
+ sortedTimePoints.forEach((ms, idx) => {
14103
+ timeToYMap.set(ms, idx * TIMELINE_LAYER_SPACING_Y);
14104
+ });
14064
14105
  const startX = -((QUEST_STATUS_ORDER.length - 1) * QUEST_LENS_COLUMN_SPACING) / 2;
14065
- Object.keys(nodeObjects).forEach((id) => {
14066
- const mesh = nodeObjects[id];
14067
- const isFilteredQuest = filteredQuests.some(
14068
- (q) => String(q.id) === String(id)
14069
- );
14070
- if (!isFilteredQuest) {
14071
- if (mesh.material) {
14072
- const fadeTween = new Tween2(mesh.material).to({ opacity: 0.15 }, 1200).easing(Easing2.Cubic.Out);
14073
- tweenGroup.add(fadeTween);
14074
- fadeTween.start();
14075
- }
14076
- if (mesh.userData.labelObject) {
14077
- mesh.userData.labelObject.visible = false;
14106
+ const positionGroups = /* @__PURE__ */ new Map();
14107
+ const NODE_X_GAP = 22;
14108
+ QUEST_STATUS_ORDER.forEach((statusName) => {
14109
+ const questsInCol = questsByStatus[statusName];
14110
+ questsInCol.forEach((q, rowIndex) => {
14111
+ const timeInfo = questTimeInfos.get(String(q.id)) || { isUndated: true };
14112
+ let nodeY;
14113
+ if (!timeInfo.isUndated) {
14114
+ nodeY = timeToYMap.get(timeInfo.startDate.getTime());
14115
+ } else {
14116
+ const undatedOffset = sortedTimePoints.length > 0 ? sortedTimePoints.length * 5 : 0;
14117
+ nodeY = -40 - rowIndex * QUEST_LENS_ROW_SPACING - undatedOffset;
14078
14118
  }
14079
- } else {
14119
+ const posKey = `${statusName}_${nodeY}`;
14120
+ if (!positionGroups.has(posKey)) positionGroups.set(posKey, []);
14121
+ positionGroups.get(posKey).push(String(q.id));
14122
+ });
14123
+ });
14124
+ Object.values(nodeObjects).forEach((mesh) => {
14125
+ const oldLabel = mesh.getObjectByName("questDateLabel") || mesh.getObjectByName("timelineLabel");
14126
+ if (oldLabel) {
14127
+ mesh.remove(oldLabel);
14128
+ if (oldLabel.material) oldLabel.material.dispose();
14129
+ }
14130
+ if (mesh.userData.timelineIntervalBar) {
14131
+ if (graphGroup) graphGroup.remove(mesh.userData.timelineIntervalBar);
14132
+ mesh.userData.timelineIntervalBar = null;
14133
+ }
14134
+ if (mesh.userData.timelineEndLabel) {
14135
+ if (graphGroup) graphGroup.remove(mesh.userData.timelineEndLabel);
14136
+ mesh.userData.timelineEndLabel = null;
14137
+ }
14138
+ });
14139
+ const nonFilteredNodeIds = Object.keys(nodeObjects).filter((id) => !filteredQuests.some((q) => String(q.id) === String(id)));
14140
+ const COMMON_START_X = startX - 100;
14141
+ const COMMON_COL_SPACING = 30;
14142
+ const COMMON_ROW_SPACING = 35;
14143
+ const MAX_COMMON_ROWS = 15;
14144
+ nonFilteredNodeIds.forEach((id, idx) => {
14145
+ const mesh = nodeObjects[id];
14146
+ if (!mesh) return;
14147
+ const col = Math.floor(idx / MAX_COMMON_ROWS);
14148
+ const row = idx % MAX_COMMON_ROWS;
14149
+ const targetX = COMMON_START_X - col * COMMON_COL_SPACING;
14150
+ const targetY = -(row * COMMON_ROW_SPACING) + 50;
14151
+ const targetZ = 0;
14152
+ if (mesh.material) {
14153
+ const opacityTween = new Tween2(mesh.material).to({ opacity: 1 }, 1200).easing(Easing2.Cubic.Out);
14154
+ tweenGroup.add(opacityTween);
14155
+ opacityTween.start();
14156
+ }
14157
+ if (mesh.userData.labelObject) {
14158
+ mesh.userData.labelObject.visible = true;
14159
+ const labelTargetPos = new THREE3.Vector3(targetX, targetY, targetZ).add(
14160
+ mesh.userData.labelOffset || new THREE3.Vector3(0, 3.8, 0)
14161
+ );
14162
+ const labelTween = new Tween2(mesh.userData.labelObject.position).to(labelTargetPos, 1500).easing(Easing2.Cubic.InOut);
14163
+ tweenGroup.add(labelTween);
14164
+ labelTween.start();
14165
+ }
14166
+ const sideTween = new Tween2(mesh.position).to({ x: targetX, y: targetY, z: targetZ }, 1500).easing(Easing2.Cubic.InOut);
14167
+ tweenGroup.add(sideTween);
14168
+ sideTween.start();
14169
+ });
14170
+ filteredQuests.forEach((q) => {
14171
+ const mesh = nodeObjects[String(q.id)];
14172
+ if (mesh) {
14080
14173
  if (mesh.material) {
14081
14174
  const fadeInTween = new Tween2(mesh.material).to({ opacity: 1 }, 1200).easing(Easing2.Cubic.Out);
14082
14175
  tweenGroup.add(fadeInTween);
@@ -14094,29 +14187,66 @@ function XViewScene({
14094
14187
  questsInCol.forEach((q, rowIndex) => {
14095
14188
  const nodeId = String(q.id);
14096
14189
  let mesh = nodeObjects[nodeId];
14097
- if (!mesh) {
14098
- mesh = addOrUpdateNodeMesh(q, new THREE3.Vector3(colX, 150, 0), true);
14099
- }
14100
14190
  const timeInfo = questTimeInfos.get(nodeId) || { isUndated: true };
14101
14191
  let targetY;
14102
14192
  if (!timeInfo.isUndated) {
14103
14193
  targetY = timeToYMap.get(timeInfo.startDate.getTime());
14104
14194
  } else {
14105
14195
  const undatedOffset = sortedTimePoints.length > 0 ? sortedTimePoints.length * 5 : 0;
14106
- targetY = -80 - rowIndex * (QUEST_LENS_ROW_SPACING || 30) - undatedOffset;
14196
+ targetY = -40 - rowIndex * QUEST_LENS_ROW_SPACING - undatedOffset;
14107
14197
  }
14108
- const lensTween = new Tween2(mesh.position).to({ x: colX, y: targetY, z: 0 }, 1500).easing(Easing2.Cubic.InOut);
14198
+ const posKey = `${statusName}_${targetY}`;
14199
+ const group = positionGroups.get(posKey) || [nodeId];
14200
+ const indexInGroup = group.indexOf(nodeId);
14201
+ const xOffset = (indexInGroup - (group.length - 1) / 2) * NODE_X_GAP;
14202
+ const finalX = colX + xOffset;
14203
+ if (!mesh) {
14204
+ mesh = addOrUpdateNodeMesh(q, new THREE3.Vector3(finalX, 150, 0), true);
14205
+ }
14206
+ const lensTween = new Tween2(mesh.position).to({ x: finalX, y: targetY, z: 0 }, 1500).easing(Easing2.Cubic.InOut);
14109
14207
  tweenGroup.add(lensTween);
14110
14208
  lensTween.start();
14111
- if (!timeInfo.isUndated && timeInfo.label) {
14209
+ if (!timeInfo.isUndated && timeInfo.label && !mesh.getObjectByName("questDateLabel")) {
14112
14210
  const dateSprite = createTextSprite(timeInfo.label, 36);
14113
14211
  dateSprite.name = "questDateLabel";
14114
14212
  dateSprite.position.set(0, -5.5, 0);
14115
14213
  mesh.add(dateSprite);
14116
14214
  }
14215
+ if (timeInfo.type === "interval" && timeToYMap.has(timeInfo.endDate.getTime())) {
14216
+ const endY = timeToYMap.get(timeInfo.endDate.getTime());
14217
+ if (Math.abs(endY - targetY) > 0.1) {
14218
+ const barHeight = Math.abs(endY - targetY);
14219
+ const barGeometry = new THREE3.CylinderGeometry(
14220
+ TIMELINE_INTERVAL_BAR_RADIUS,
14221
+ TIMELINE_INTERVAL_BAR_RADIUS,
14222
+ 1,
14223
+ 16
14224
+ );
14225
+ const barMaterial = new THREE3.MeshStandardMaterial({
14226
+ color: TIMELINE_GOLD_COLOR,
14227
+ emissive: 12092939,
14228
+ emissiveIntensity: 0.4,
14229
+ transparent: true,
14230
+ opacity: TIMELINE_INTERVAL_BAR_OPACITY,
14231
+ roughness: 0.3,
14232
+ metalness: 0.8
14233
+ });
14234
+ const intervalBar = new THREE3.Mesh(barGeometry, barMaterial);
14235
+ intervalBar.userData.associatedNodeId = mesh.userData.id;
14236
+ intervalBar.position.set(finalX, (targetY + endY) / 2, -0.5);
14237
+ intervalBar.scale.y = barHeight;
14238
+ graphGroup.add(intervalBar);
14239
+ mesh.userData.timelineIntervalBar = intervalBar;
14240
+ const endLabel = createTextSprite(timeInfo.label.split(" - ")[1] || "", 30);
14241
+ endLabel.name = "timelineEndLabel";
14242
+ endLabel.position.set(finalX, endY + 4, 0);
14243
+ graphGroup.add(endLabel);
14244
+ mesh.userData.timelineEndLabel = endLabel;
14245
+ }
14246
+ }
14117
14247
  if (mesh.userData.labelObject) {
14118
- const labelTargetPos = new THREE3.Vector3(colX, targetY, 0).add(
14119
- mesh.userData.labelOffset || new THREE3.Vector3(0, -2.5, 0)
14248
+ const labelTargetPos = new THREE3.Vector3(finalX, targetY, 0).add(
14249
+ mesh.userData.labelOffset || new THREE3.Vector3(0, 3.8, 0)
14120
14250
  );
14121
14251
  const labelTween = new Tween2(mesh.userData.labelObject.position).to(labelTargetPos, 1500).easing(Easing2.Cubic.InOut);
14122
14252
  tweenGroup.add(labelTween);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lv-x-software-house/x_view",
3
- "version": "1.2.5-dev.43",
3
+ "version": "1.2.5-dev.45",
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",