@lv-x-software-house/x_view 1.2.6-dev.49 → 1.2.6-dev.51

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 +222 -20
  2. package/dist/index.mjs +222 -20
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -3896,6 +3896,8 @@ function XViewSidebar({
3896
3896
  },
3897
3897
  milestones = [],
3898
3898
  onOpenMilestoneManager = () => {
3899
+ },
3900
+ onActivateReshapeLens = () => {
3899
3901
  }
3900
3902
  }) {
3901
3903
  const [query, setQuery] = (0, import_react3.useState)("");
@@ -3910,6 +3912,10 @@ function XViewSidebar({
3910
3912
  const [questFilterStatus, setQuestFilterStatus] = (0, import_react3.useState)("");
3911
3913
  const [isQuestLensMenuOpen, setIsQuestLensMenuOpen] = (0, import_react3.useState)(false);
3912
3914
  const [questFilterMilestone, setQuestFilterMilestone] = (0, import_react3.useState)("");
3915
+ const [isReshapeLensMenuOpen, setIsReshapeLensMenuOpen] = (0, import_react3.useState)(false);
3916
+ const [reshapeLayoutType, setReshapeLayoutType] = (0, import_react3.useState)("Star");
3917
+ const [reshapeColumnCount, setReshapeColumnCount] = (0, import_react3.useState)(4);
3918
+ const [reshapeCenterNodeId, setReshapeCenterNodeId] = (0, import_react3.useState)("");
3913
3919
  const [isTypeDropdownOpen, setIsTypeDropdownOpen] = (0, import_react3.useState)(false);
3914
3920
  const [highlightedTypeIndex, setHighlightedTypeIndex] = (0, import_react3.useState)(-1);
3915
3921
  const containerRef = (0, import_react3.useRef)(null);
@@ -3917,6 +3923,7 @@ function XViewSidebar({
3917
3923
  const filterMenuRef = (0, import_react3.useRef)(null);
3918
3924
  const typeDropdownRef = (0, import_react3.useRef)(null);
3919
3925
  const questLensRef = (0, import_react3.useRef)(null);
3926
+ const reshapeLensRef = (0, import_react3.useRef)(null);
3920
3927
  const ability = (0, import_react3.useMemo)(() => {
3921
3928
  return defineAbilityFor(userRole);
3922
3929
  }, [userRole]);
@@ -4022,16 +4029,19 @@ function XViewSidebar({
4022
4029
  return () => document.removeEventListener("mousedown", handleClickOutside);
4023
4030
  }, [isFilterMenuOpen, isTypeDropdownOpen]);
4024
4031
  (0, import_react3.useEffect)(() => {
4025
- const handleClickOutsideQuest = (event) => {
4032
+ const handleClickOutsideQuestAndReshape = (event) => {
4026
4033
  if (questLensRef.current && !questLensRef.current.contains(event.target)) {
4027
4034
  setIsQuestLensMenuOpen(false);
4028
4035
  }
4036
+ if (reshapeLensRef.current && !reshapeLensRef.current.contains(event.target)) {
4037
+ setIsReshapeLensMenuOpen(false);
4038
+ }
4029
4039
  };
4030
- if (isQuestLensMenuOpen) {
4031
- document.addEventListener("mousedown", handleClickOutsideQuest);
4040
+ if (isQuestLensMenuOpen || isReshapeLensMenuOpen) {
4041
+ document.addEventListener("mousedown", handleClickOutsideQuestAndReshape);
4032
4042
  }
4033
- return () => document.removeEventListener("mousedown", handleClickOutsideQuest);
4034
- }, [isQuestLensMenuOpen]);
4043
+ return () => document.removeEventListener("mousedown", handleClickOutsideQuestAndReshape);
4044
+ }, [isQuestLensMenuOpen, isReshapeLensMenuOpen]);
4035
4045
  (0, import_react3.useEffect)(() => {
4036
4046
  if (!query) setSelectedNodeId(null);
4037
4047
  }, [query]);
@@ -4371,6 +4381,71 @@ function XViewSidebar({
4371
4381
  className: "flex-[2] py-2 bg-indigo-600 hover:bg-indigo-500 text-white text-[11px] font-bold rounded-lg shadow-lg shadow-indigo-500/20 transition-all hover:scale-[1.02] active:scale-95"
4372
4382
  },
4373
4383
  "Aplicar Lente"
4384
+ )))), /* @__PURE__ */ import_react3.default.createElement("div", { className: "relative", ref: reshapeLensRef }, /* @__PURE__ */ import_react3.default.createElement(
4385
+ "button",
4386
+ {
4387
+ onClick: () => setIsReshapeLensMenuOpen(!isReshapeLensMenuOpen),
4388
+ className: `${buttonBaseClass} ${isReshapeLensMenuOpen ? "bg-indigo-500/30 border-indigo-400" : ""}`,
4389
+ title: "Abrir op\xE7\xF5es de Reshape"
4390
+ },
4391
+ /* @__PURE__ */ import_react3.default.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ import_react3.default.createElement("polygon", { points: "12 2 2 7 12 12 22 7 12 2" }), /* @__PURE__ */ import_react3.default.createElement("polyline", { points: "2 17 12 22 22 17" }), /* @__PURE__ */ import_react3.default.createElement("polyline", { points: "2 12 12 17 22 12" })),
4392
+ "Reshape Lens"
4393
+ ), isReshapeLensMenuOpen && /* @__PURE__ */ import_react3.default.createElement("div", { className: "absolute left-0 bottom-full mb-2 w-full p-4 rounded-xl bg-slate-900 border border-white/10 shadow-2xl z-50 flex flex-col gap-4 animate-in fade-in slide-in-from-bottom-2 duration-200" }, /* @__PURE__ */ import_react3.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ import_react3.default.createElement("label", { className: "text-[10px] uppercase text-slate-400 font-bold tracking-widest" }, "Layout Type"), /* @__PURE__ */ import_react3.default.createElement(
4394
+ "select",
4395
+ {
4396
+ value: reshapeLayoutType,
4397
+ onChange: (e) => setReshapeLayoutType(e.target.value),
4398
+ className: "w-full bg-slate-800 p-2 text-xs rounded border border-white/10 focus:outline-none focus:border-indigo-500/50 text-white appearance-none cursor-pointer"
4399
+ },
4400
+ /* @__PURE__ */ import_react3.default.createElement("option", { value: "Star" }, "Star"),
4401
+ /* @__PURE__ */ import_react3.default.createElement("option", { value: "Circle" }, "Circle"),
4402
+ /* @__PURE__ */ import_react3.default.createElement("option", { value: "Column" }, "Column")
4403
+ )), reshapeLayoutType === "Column" && /* @__PURE__ */ import_react3.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ import_react3.default.createElement("label", { className: "text-[10px] uppercase text-slate-400 font-bold tracking-widest" }, "Colunas"), /* @__PURE__ */ import_react3.default.createElement(
4404
+ "input",
4405
+ {
4406
+ type: "number",
4407
+ min: "1",
4408
+ max: "12",
4409
+ value: reshapeColumnCount,
4410
+ onChange: (e) => setReshapeColumnCount(parseInt(e.target.value) || 4),
4411
+ className: "w-full bg-slate-800 p-2 text-xs rounded border border-white/10 focus:outline-none focus:border-indigo-500/50 text-white"
4412
+ }
4413
+ )), reshapeLayoutType === "Star" && /* @__PURE__ */ import_react3.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ import_react3.default.createElement("label", { className: "text-[10px] uppercase text-slate-400 font-bold tracking-widest" }, "N\xF3 Central"), /* @__PURE__ */ import_react3.default.createElement(
4414
+ "select",
4415
+ {
4416
+ value: reshapeCenterNodeId,
4417
+ onChange: (e) => setReshapeCenterNodeId(e.target.value),
4418
+ className: "w-full bg-slate-800 p-2 text-xs rounded border border-white/10 focus:outline-none focus:border-indigo-500/50 text-white appearance-none cursor-pointer"
4419
+ },
4420
+ /* @__PURE__ */ import_react3.default.createElement("option", { value: "" }, "Selecione..."),
4421
+ (Array.isArray(dbNodes) ? dbNodes.filter((n) => isNodeInView(n.id)) : []).map((n) => /* @__PURE__ */ import_react3.default.createElement("option", { key: n.id, value: n.id }, n.name))
4422
+ )), /* @__PURE__ */ import_react3.default.createElement("div", { className: "flex gap-2 pt-1" }, /* @__PURE__ */ import_react3.default.createElement(
4423
+ "button",
4424
+ {
4425
+ onClick: () => {
4426
+ setReshapeLayoutType("Star");
4427
+ setReshapeColumnCount(4);
4428
+ setReshapeCenterNodeId("");
4429
+ },
4430
+ className: "flex-1 py-2 bg-white/5 hover:bg-white/10 text-slate-300 text-[11px] font-medium rounded-lg transition-colors border border-white/5"
4431
+ },
4432
+ "Resetar"
4433
+ ), /* @__PURE__ */ import_react3.default.createElement(
4434
+ "button",
4435
+ {
4436
+ onClick: () => {
4437
+ onActivateReshapeLens({
4438
+ type: reshapeLayoutType,
4439
+ columns: reshapeColumnCount,
4440
+ centerNodeId: reshapeCenterNodeId
4441
+ });
4442
+ setIsReshapeLensMenuOpen(false);
4443
+ onToggle(false);
4444
+ },
4445
+ disabled: reshapeLayoutType === "Star" && !reshapeCenterNodeId,
4446
+ className: "flex-[2] py-2 bg-indigo-600 hover:bg-indigo-500 text-white text-[11px] font-bold rounded-lg shadow-lg shadow-indigo-500/20 transition-all hover:scale-[1.02] active:scale-95 disabled:opacity-50 disabled:cursor-not-allowed"
4447
+ },
4448
+ "Aplicar Lente"
4374
4449
  )))), /* @__PURE__ */ import_react3.default.createElement(
4375
4450
  "button",
4376
4451
  {
@@ -15132,6 +15207,127 @@ function XViewScene({
15132
15207
  },
15133
15208
  []
15134
15209
  );
15210
+ const handleActivateReshapeLens = (0, import_react31.useCallback)(({ type, columns, centerNodeId }) => {
15211
+ const { nodeObjects, tweenGroup, camera, controls } = stateRef.current;
15212
+ if (!nodeObjects || !tweenGroup || !camera || !controls) return;
15213
+ const activeNodeIds = Object.keys(nodeObjects);
15214
+ if (activeNodeIds.length === 0) return;
15215
+ let sumX = 0;
15216
+ let sumY = 0;
15217
+ let nodeCount = 0;
15218
+ activeNodeIds.forEach((id) => {
15219
+ const mesh = nodeObjects[id];
15220
+ if (mesh && mesh.material) {
15221
+ const opacityTween = new import_tween2.Tween(mesh.material).to({ opacity: 1 }, 1200).easing(import_tween2.Easing.Cubic.Out);
15222
+ tweenGroup.add(opacityTween);
15223
+ opacityTween.start();
15224
+ }
15225
+ if (mesh && mesh.userData && mesh.userData.labelObject) {
15226
+ mesh.userData.labelObject.visible = true;
15227
+ }
15228
+ });
15229
+ if (type === "Column") {
15230
+ const colSpacing = 80;
15231
+ const rowSpacing = 80;
15232
+ const numCols = columns || 4;
15233
+ activeNodeIds.forEach((id, index) => {
15234
+ const mesh = nodeObjects[id];
15235
+ const col = index % numCols;
15236
+ const row = Math.floor(index / numCols);
15237
+ const targetX = (col - (numCols - 1) / 2) * colSpacing;
15238
+ const targetY = -(row * rowSpacing) + 50;
15239
+ const posTween = new import_tween2.Tween(mesh.position).to({ x: targetX, y: targetY, z: 0 }, 1500).easing(import_tween2.Easing.Cubic.InOut);
15240
+ tweenGroup.add(posTween);
15241
+ posTween.start();
15242
+ if (mesh.userData.labelObject) {
15243
+ const labelTarget = new THREE3.Vector3(targetX, targetY, 0).add(mesh.userData.labelOffset || new THREE3.Vector3(0, 3.8, 0));
15244
+ const lblTween = new import_tween2.Tween(mesh.userData.labelObject.position).to(labelTarget, 1500).easing(import_tween2.Easing.Cubic.InOut);
15245
+ tweenGroup.add(lblTween);
15246
+ lblTween.start();
15247
+ }
15248
+ sumX += targetX;
15249
+ sumY += targetY;
15250
+ nodeCount++;
15251
+ });
15252
+ } else if (type === "Circle") {
15253
+ const total = activeNodeIds.length;
15254
+ const radius = Math.max(80, total * 8);
15255
+ activeNodeIds.forEach((id, index) => {
15256
+ const mesh = nodeObjects[id];
15257
+ const angle = index / total * Math.PI * 2;
15258
+ const targetX = Math.cos(angle) * radius;
15259
+ const targetY = Math.sin(angle) * radius;
15260
+ const posTween = new import_tween2.Tween(mesh.position).to({ x: targetX, y: targetY, z: 0 }, 1500).easing(import_tween2.Easing.Cubic.InOut);
15261
+ tweenGroup.add(posTween);
15262
+ posTween.start();
15263
+ if (mesh.userData.labelObject) {
15264
+ const labelTarget = new THREE3.Vector3(targetX, targetY, 0).add(mesh.userData.labelOffset || new THREE3.Vector3(0, 3.8, 0));
15265
+ const lblTween = new import_tween2.Tween(mesh.userData.labelObject.position).to(labelTarget, 1500).easing(import_tween2.Easing.Cubic.InOut);
15266
+ tweenGroup.add(lblTween);
15267
+ lblTween.start();
15268
+ }
15269
+ sumX += targetX;
15270
+ sumY += targetY;
15271
+ nodeCount++;
15272
+ });
15273
+ } else if (type === "Star") {
15274
+ const centerMesh = centerNodeId ? nodeObjects[String(centerNodeId)] : null;
15275
+ const orbitingNodeIds = activeNodeIds.filter((id) => id !== String(centerNodeId));
15276
+ const totalOrbiting = orbitingNodeIds.length;
15277
+ const radius = Math.max(80, totalOrbiting * 10);
15278
+ if (centerMesh) {
15279
+ const posTween = new import_tween2.Tween(centerMesh.position).to({ x: 0, y: 0, z: 0 }, 1500).easing(import_tween2.Easing.Cubic.InOut);
15280
+ tweenGroup.add(posTween);
15281
+ posTween.start();
15282
+ if (centerMesh.userData.labelObject) {
15283
+ const labelTarget = new THREE3.Vector3(0, 0, 0).add(centerMesh.userData.labelOffset || new THREE3.Vector3(0, 3.8, 0));
15284
+ const lblTween = new import_tween2.Tween(centerMesh.userData.labelObject.position).to(labelTarget, 1500).easing(import_tween2.Easing.Cubic.InOut);
15285
+ tweenGroup.add(lblTween);
15286
+ lblTween.start();
15287
+ }
15288
+ sumX += 0;
15289
+ sumY += 0;
15290
+ nodeCount++;
15291
+ }
15292
+ orbitingNodeIds.forEach((id, index) => {
15293
+ const mesh = nodeObjects[id];
15294
+ const angle = totalOrbiting > 0 ? index / totalOrbiting * Math.PI * 2 : 0;
15295
+ const targetX = Math.cos(angle) * radius;
15296
+ const targetY = Math.sin(angle) * radius;
15297
+ const posTween = new import_tween2.Tween(mesh.position).to({ x: targetX, y: targetY, z: 0 }, 1500).easing(import_tween2.Easing.Cubic.InOut);
15298
+ tweenGroup.add(posTween);
15299
+ posTween.start();
15300
+ if (mesh.userData.labelObject) {
15301
+ const labelTarget = new THREE3.Vector3(targetX, targetY, 0).add(mesh.userData.labelOffset || new THREE3.Vector3(0, 3.8, 0));
15302
+ const lblTween = new import_tween2.Tween(mesh.userData.labelObject.position).to(labelTarget, 1500).easing(import_tween2.Easing.Cubic.InOut);
15303
+ tweenGroup.add(lblTween);
15304
+ lblTween.start();
15305
+ }
15306
+ sumX += targetX;
15307
+ sumY += targetY;
15308
+ nodeCount++;
15309
+ });
15310
+ }
15311
+ if (nodeCount > 0) {
15312
+ const centerX = sumX / nodeCount;
15313
+ const centerY = sumY / nodeCount;
15314
+ const targetFocus = new THREE3.Vector3(centerX, centerY, 0);
15315
+ const targetTween = new import_tween2.Tween(controls.target).to({ x: targetFocus.x, y: targetFocus.y, z: targetFocus.z }, 1500).easing(import_tween2.Easing.Cubic.InOut);
15316
+ tweenGroup.add(targetTween);
15317
+ targetTween.start();
15318
+ let zoomZ = 140;
15319
+ if (type === "Column") {
15320
+ const numRows = Math.ceil(nodeCount / (columns || 4));
15321
+ zoomZ = Math.max(140, numRows * 50);
15322
+ } else if (type === "Circle" || type === "Star") {
15323
+ const radius = Math.max(80, nodeCount * 8);
15324
+ zoomZ = radius * 2.5;
15325
+ }
15326
+ const camTween = new import_tween2.Tween(camera.position).to({ x: targetFocus.x, y: targetFocus.y, z: zoomZ }, 1500).easing(import_tween2.Easing.Cubic.InOut);
15327
+ tweenGroup.add(camTween);
15328
+ camTween.start();
15329
+ }
15330
+ }, []);
15135
15331
  const handleActivateQuestLens = (0, import_react31.useCallback)(({ assigneeId, status: status2, milestoneId }) => {
15136
15332
  var _a2;
15137
15333
  const { nodeObjects, tweenGroup, graphGroup } = stateRef.current;
@@ -16023,31 +16219,36 @@ function XViewScene({
16023
16219
  (_b2 = stateRef.current.tweenGroup) == null ? void 0 : _b2.update(time);
16024
16220
  const { keysPressed, controls: sceneControls, camera: sceneCamera } = stateRef.current;
16025
16221
  if (sceneControls && keysPressed) {
16026
- const panSpeed = 0.5;
16222
+ const panSpeed = 1.5;
16027
16223
  const rotateSpeed = 0.02;
16028
16224
  const zoomInFactor = 0.98;
16029
16225
  const zoomOutFactor = 1.02;
16030
16226
  const isShift = !!keysPressed["shiftKey"];
16031
16227
  const isCtrl = !!keysPressed["ctrlKey"];
16032
- if (isCtrl) {
16228
+ const dirX = (keysPressed["ArrowRight"] ? 1 : 0) - (keysPressed["ArrowLeft"] ? 1 : 0);
16229
+ const dirY = (keysPressed["ArrowUp"] ? 1 : 0) - (keysPressed["ArrowDown"] ? 1 : 0);
16230
+ if (isCtrl && (dirX !== 0 || dirY !== 0)) {
16033
16231
  const offset = sceneCamera.position.clone().sub(sceneControls.target);
16034
16232
  const spherical = new THREE3.Spherical().setFromVector3(offset);
16035
- if (keysPressed["ArrowLeft"]) spherical.theta += rotateSpeed;
16036
- if (keysPressed["ArrowRight"]) spherical.theta -= rotateSpeed;
16037
- if (keysPressed["ArrowUp"]) spherical.phi -= rotateSpeed;
16038
- if (keysPressed["ArrowDown"]) spherical.phi += rotateSpeed;
16233
+ spherical.theta += dirX * rotateSpeed;
16234
+ spherical.phi -= dirY * rotateSpeed;
16039
16235
  spherical.makeSafe();
16040
16236
  offset.setFromSpherical(spherical);
16041
16237
  sceneCamera.position.copy(sceneControls.target).add(offset);
16042
- } else if (isShift) {
16043
- if (keysPressed["ArrowUp"]) sceneControls.dollyIn(zoomInFactor);
16044
- if (keysPressed["ArrowDown"]) sceneControls.dollyOut(zoomOutFactor);
16045
- } else {
16046
- const panStep = 10;
16047
- if (keysPressed["ArrowLeft"]) sceneControls.pan(panStep, 0);
16048
- if (keysPressed["ArrowRight"]) sceneControls.pan(-panStep, 0);
16049
- if (keysPressed["ArrowUp"]) sceneControls.pan(0, panStep);
16050
- if (keysPressed["ArrowDown"]) sceneControls.pan(0, -panStep);
16238
+ } else if (isShift && dirY !== 0) {
16239
+ const factor = keysPressed["ArrowUp"] ? zoomInFactor : zoomOutFactor;
16240
+ const offset = sceneCamera.position.clone().sub(sceneControls.target);
16241
+ offset.multiplyScalar(factor);
16242
+ sceneCamera.position.copy(sceneControls.target).add(offset);
16243
+ } else if (!isCtrl && !isShift && (dirX !== 0 || dirY !== 0)) {
16244
+ const distance = sceneCamera.position.distanceTo(sceneControls.target);
16245
+ const currentPanSpeed = panSpeed * (distance / 100);
16246
+ const vecX = new THREE3.Vector3().setFromMatrixColumn(sceneCamera.matrix, 0);
16247
+ const vecY = new THREE3.Vector3().setFromMatrixColumn(sceneCamera.matrix, 1);
16248
+ vecX.multiplyScalar(dirX * currentPanSpeed);
16249
+ vecY.multiplyScalar(dirY * currentPanSpeed);
16250
+ sceneCamera.position.add(vecX).add(vecY);
16251
+ sceneControls.target.add(vecX).add(vecY);
16051
16252
  }
16052
16253
  }
16053
16254
  stateRef.current.controls.update();
@@ -18698,6 +18899,7 @@ function XViewScene({
18698
18899
  importSuccessMessage,
18699
18900
  onActivateTimeline: handleActivateTimeline,
18700
18901
  onActivateQuestLens: handleActivateQuestLens,
18902
+ onActivateReshapeLens: handleActivateReshapeLens,
18701
18903
  onOpenBoards: handleOpenBoards,
18702
18904
  viewType: viewParams == null ? void 0 : viewParams.type,
18703
18905
  viewName: viewParams == null ? void 0 : viewParams.name,
package/dist/index.mjs CHANGED
@@ -3857,6 +3857,8 @@ function XViewSidebar({
3857
3857
  },
3858
3858
  milestones = [],
3859
3859
  onOpenMilestoneManager = () => {
3860
+ },
3861
+ onActivateReshapeLens = () => {
3860
3862
  }
3861
3863
  }) {
3862
3864
  const [query, setQuery] = useState3("");
@@ -3871,6 +3873,10 @@ function XViewSidebar({
3871
3873
  const [questFilterStatus, setQuestFilterStatus] = useState3("");
3872
3874
  const [isQuestLensMenuOpen, setIsQuestLensMenuOpen] = useState3(false);
3873
3875
  const [questFilterMilestone, setQuestFilterMilestone] = useState3("");
3876
+ const [isReshapeLensMenuOpen, setIsReshapeLensMenuOpen] = useState3(false);
3877
+ const [reshapeLayoutType, setReshapeLayoutType] = useState3("Star");
3878
+ const [reshapeColumnCount, setReshapeColumnCount] = useState3(4);
3879
+ const [reshapeCenterNodeId, setReshapeCenterNodeId] = useState3("");
3874
3880
  const [isTypeDropdownOpen, setIsTypeDropdownOpen] = useState3(false);
3875
3881
  const [highlightedTypeIndex, setHighlightedTypeIndex] = useState3(-1);
3876
3882
  const containerRef = useRef2(null);
@@ -3878,6 +3884,7 @@ function XViewSidebar({
3878
3884
  const filterMenuRef = useRef2(null);
3879
3885
  const typeDropdownRef = useRef2(null);
3880
3886
  const questLensRef = useRef2(null);
3887
+ const reshapeLensRef = useRef2(null);
3881
3888
  const ability = useMemo2(() => {
3882
3889
  return defineAbilityFor(userRole);
3883
3890
  }, [userRole]);
@@ -3983,16 +3990,19 @@ function XViewSidebar({
3983
3990
  return () => document.removeEventListener("mousedown", handleClickOutside);
3984
3991
  }, [isFilterMenuOpen, isTypeDropdownOpen]);
3985
3992
  useEffect2(() => {
3986
- const handleClickOutsideQuest = (event) => {
3993
+ const handleClickOutsideQuestAndReshape = (event) => {
3987
3994
  if (questLensRef.current && !questLensRef.current.contains(event.target)) {
3988
3995
  setIsQuestLensMenuOpen(false);
3989
3996
  }
3997
+ if (reshapeLensRef.current && !reshapeLensRef.current.contains(event.target)) {
3998
+ setIsReshapeLensMenuOpen(false);
3999
+ }
3990
4000
  };
3991
- if (isQuestLensMenuOpen) {
3992
- document.addEventListener("mousedown", handleClickOutsideQuest);
4001
+ if (isQuestLensMenuOpen || isReshapeLensMenuOpen) {
4002
+ document.addEventListener("mousedown", handleClickOutsideQuestAndReshape);
3993
4003
  }
3994
- return () => document.removeEventListener("mousedown", handleClickOutsideQuest);
3995
- }, [isQuestLensMenuOpen]);
4004
+ return () => document.removeEventListener("mousedown", handleClickOutsideQuestAndReshape);
4005
+ }, [isQuestLensMenuOpen, isReshapeLensMenuOpen]);
3996
4006
  useEffect2(() => {
3997
4007
  if (!query) setSelectedNodeId(null);
3998
4008
  }, [query]);
@@ -4332,6 +4342,71 @@ function XViewSidebar({
4332
4342
  className: "flex-[2] py-2 bg-indigo-600 hover:bg-indigo-500 text-white text-[11px] font-bold rounded-lg shadow-lg shadow-indigo-500/20 transition-all hover:scale-[1.02] active:scale-95"
4333
4343
  },
4334
4344
  "Aplicar Lente"
4345
+ )))), /* @__PURE__ */ React2.createElement("div", { className: "relative", ref: reshapeLensRef }, /* @__PURE__ */ React2.createElement(
4346
+ "button",
4347
+ {
4348
+ onClick: () => setIsReshapeLensMenuOpen(!isReshapeLensMenuOpen),
4349
+ className: `${buttonBaseClass} ${isReshapeLensMenuOpen ? "bg-indigo-500/30 border-indigo-400" : ""}`,
4350
+ title: "Abrir op\xE7\xF5es de Reshape"
4351
+ },
4352
+ /* @__PURE__ */ React2.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React2.createElement("polygon", { points: "12 2 2 7 12 12 22 7 12 2" }), /* @__PURE__ */ React2.createElement("polyline", { points: "2 17 12 22 22 17" }), /* @__PURE__ */ React2.createElement("polyline", { points: "2 12 12 17 22 12" })),
4353
+ "Reshape Lens"
4354
+ ), isReshapeLensMenuOpen && /* @__PURE__ */ React2.createElement("div", { className: "absolute left-0 bottom-full mb-2 w-full p-4 rounded-xl bg-slate-900 border border-white/10 shadow-2xl z-50 flex flex-col gap-4 animate-in fade-in slide-in-from-bottom-2 duration-200" }, /* @__PURE__ */ React2.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React2.createElement("label", { className: "text-[10px] uppercase text-slate-400 font-bold tracking-widest" }, "Layout Type"), /* @__PURE__ */ React2.createElement(
4355
+ "select",
4356
+ {
4357
+ value: reshapeLayoutType,
4358
+ onChange: (e) => setReshapeLayoutType(e.target.value),
4359
+ className: "w-full bg-slate-800 p-2 text-xs rounded border border-white/10 focus:outline-none focus:border-indigo-500/50 text-white appearance-none cursor-pointer"
4360
+ },
4361
+ /* @__PURE__ */ React2.createElement("option", { value: "Star" }, "Star"),
4362
+ /* @__PURE__ */ React2.createElement("option", { value: "Circle" }, "Circle"),
4363
+ /* @__PURE__ */ React2.createElement("option", { value: "Column" }, "Column")
4364
+ )), reshapeLayoutType === "Column" && /* @__PURE__ */ React2.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React2.createElement("label", { className: "text-[10px] uppercase text-slate-400 font-bold tracking-widest" }, "Colunas"), /* @__PURE__ */ React2.createElement(
4365
+ "input",
4366
+ {
4367
+ type: "number",
4368
+ min: "1",
4369
+ max: "12",
4370
+ value: reshapeColumnCount,
4371
+ onChange: (e) => setReshapeColumnCount(parseInt(e.target.value) || 4),
4372
+ className: "w-full bg-slate-800 p-2 text-xs rounded border border-white/10 focus:outline-none focus:border-indigo-500/50 text-white"
4373
+ }
4374
+ )), reshapeLayoutType === "Star" && /* @__PURE__ */ React2.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React2.createElement("label", { className: "text-[10px] uppercase text-slate-400 font-bold tracking-widest" }, "N\xF3 Central"), /* @__PURE__ */ React2.createElement(
4375
+ "select",
4376
+ {
4377
+ value: reshapeCenterNodeId,
4378
+ onChange: (e) => setReshapeCenterNodeId(e.target.value),
4379
+ className: "w-full bg-slate-800 p-2 text-xs rounded border border-white/10 focus:outline-none focus:border-indigo-500/50 text-white appearance-none cursor-pointer"
4380
+ },
4381
+ /* @__PURE__ */ React2.createElement("option", { value: "" }, "Selecione..."),
4382
+ (Array.isArray(dbNodes) ? dbNodes.filter((n) => isNodeInView(n.id)) : []).map((n) => /* @__PURE__ */ React2.createElement("option", { key: n.id, value: n.id }, n.name))
4383
+ )), /* @__PURE__ */ React2.createElement("div", { className: "flex gap-2 pt-1" }, /* @__PURE__ */ React2.createElement(
4384
+ "button",
4385
+ {
4386
+ onClick: () => {
4387
+ setReshapeLayoutType("Star");
4388
+ setReshapeColumnCount(4);
4389
+ setReshapeCenterNodeId("");
4390
+ },
4391
+ className: "flex-1 py-2 bg-white/5 hover:bg-white/10 text-slate-300 text-[11px] font-medium rounded-lg transition-colors border border-white/5"
4392
+ },
4393
+ "Resetar"
4394
+ ), /* @__PURE__ */ React2.createElement(
4395
+ "button",
4396
+ {
4397
+ onClick: () => {
4398
+ onActivateReshapeLens({
4399
+ type: reshapeLayoutType,
4400
+ columns: reshapeColumnCount,
4401
+ centerNodeId: reshapeCenterNodeId
4402
+ });
4403
+ setIsReshapeLensMenuOpen(false);
4404
+ onToggle(false);
4405
+ },
4406
+ disabled: reshapeLayoutType === "Star" && !reshapeCenterNodeId,
4407
+ className: "flex-[2] py-2 bg-indigo-600 hover:bg-indigo-500 text-white text-[11px] font-bold rounded-lg shadow-lg shadow-indigo-500/20 transition-all hover:scale-[1.02] active:scale-95 disabled:opacity-50 disabled:cursor-not-allowed"
4408
+ },
4409
+ "Aplicar Lente"
4335
4410
  )))), /* @__PURE__ */ React2.createElement(
4336
4411
  "button",
4337
4412
  {
@@ -15188,6 +15263,127 @@ function XViewScene({
15188
15263
  },
15189
15264
  []
15190
15265
  );
15266
+ const handleActivateReshapeLens = useCallback6(({ type, columns, centerNodeId }) => {
15267
+ const { nodeObjects, tweenGroup, camera, controls } = stateRef.current;
15268
+ if (!nodeObjects || !tweenGroup || !camera || !controls) return;
15269
+ const activeNodeIds = Object.keys(nodeObjects);
15270
+ if (activeNodeIds.length === 0) return;
15271
+ let sumX = 0;
15272
+ let sumY = 0;
15273
+ let nodeCount = 0;
15274
+ activeNodeIds.forEach((id) => {
15275
+ const mesh = nodeObjects[id];
15276
+ if (mesh && mesh.material) {
15277
+ const opacityTween = new Tween2(mesh.material).to({ opacity: 1 }, 1200).easing(Easing2.Cubic.Out);
15278
+ tweenGroup.add(opacityTween);
15279
+ opacityTween.start();
15280
+ }
15281
+ if (mesh && mesh.userData && mesh.userData.labelObject) {
15282
+ mesh.userData.labelObject.visible = true;
15283
+ }
15284
+ });
15285
+ if (type === "Column") {
15286
+ const colSpacing = 80;
15287
+ const rowSpacing = 80;
15288
+ const numCols = columns || 4;
15289
+ activeNodeIds.forEach((id, index) => {
15290
+ const mesh = nodeObjects[id];
15291
+ const col = index % numCols;
15292
+ const row = Math.floor(index / numCols);
15293
+ const targetX = (col - (numCols - 1) / 2) * colSpacing;
15294
+ const targetY = -(row * rowSpacing) + 50;
15295
+ const posTween = new Tween2(mesh.position).to({ x: targetX, y: targetY, z: 0 }, 1500).easing(Easing2.Cubic.InOut);
15296
+ tweenGroup.add(posTween);
15297
+ posTween.start();
15298
+ if (mesh.userData.labelObject) {
15299
+ const labelTarget = new THREE3.Vector3(targetX, targetY, 0).add(mesh.userData.labelOffset || new THREE3.Vector3(0, 3.8, 0));
15300
+ const lblTween = new Tween2(mesh.userData.labelObject.position).to(labelTarget, 1500).easing(Easing2.Cubic.InOut);
15301
+ tweenGroup.add(lblTween);
15302
+ lblTween.start();
15303
+ }
15304
+ sumX += targetX;
15305
+ sumY += targetY;
15306
+ nodeCount++;
15307
+ });
15308
+ } else if (type === "Circle") {
15309
+ const total = activeNodeIds.length;
15310
+ const radius = Math.max(80, total * 8);
15311
+ activeNodeIds.forEach((id, index) => {
15312
+ const mesh = nodeObjects[id];
15313
+ const angle = index / total * Math.PI * 2;
15314
+ const targetX = Math.cos(angle) * radius;
15315
+ const targetY = Math.sin(angle) * radius;
15316
+ const posTween = new Tween2(mesh.position).to({ x: targetX, y: targetY, z: 0 }, 1500).easing(Easing2.Cubic.InOut);
15317
+ tweenGroup.add(posTween);
15318
+ posTween.start();
15319
+ if (mesh.userData.labelObject) {
15320
+ const labelTarget = new THREE3.Vector3(targetX, targetY, 0).add(mesh.userData.labelOffset || new THREE3.Vector3(0, 3.8, 0));
15321
+ const lblTween = new Tween2(mesh.userData.labelObject.position).to(labelTarget, 1500).easing(Easing2.Cubic.InOut);
15322
+ tweenGroup.add(lblTween);
15323
+ lblTween.start();
15324
+ }
15325
+ sumX += targetX;
15326
+ sumY += targetY;
15327
+ nodeCount++;
15328
+ });
15329
+ } else if (type === "Star") {
15330
+ const centerMesh = centerNodeId ? nodeObjects[String(centerNodeId)] : null;
15331
+ const orbitingNodeIds = activeNodeIds.filter((id) => id !== String(centerNodeId));
15332
+ const totalOrbiting = orbitingNodeIds.length;
15333
+ const radius = Math.max(80, totalOrbiting * 10);
15334
+ if (centerMesh) {
15335
+ const posTween = new Tween2(centerMesh.position).to({ x: 0, y: 0, z: 0 }, 1500).easing(Easing2.Cubic.InOut);
15336
+ tweenGroup.add(posTween);
15337
+ posTween.start();
15338
+ if (centerMesh.userData.labelObject) {
15339
+ const labelTarget = new THREE3.Vector3(0, 0, 0).add(centerMesh.userData.labelOffset || new THREE3.Vector3(0, 3.8, 0));
15340
+ const lblTween = new Tween2(centerMesh.userData.labelObject.position).to(labelTarget, 1500).easing(Easing2.Cubic.InOut);
15341
+ tweenGroup.add(lblTween);
15342
+ lblTween.start();
15343
+ }
15344
+ sumX += 0;
15345
+ sumY += 0;
15346
+ nodeCount++;
15347
+ }
15348
+ orbitingNodeIds.forEach((id, index) => {
15349
+ const mesh = nodeObjects[id];
15350
+ const angle = totalOrbiting > 0 ? index / totalOrbiting * Math.PI * 2 : 0;
15351
+ const targetX = Math.cos(angle) * radius;
15352
+ const targetY = Math.sin(angle) * radius;
15353
+ const posTween = new Tween2(mesh.position).to({ x: targetX, y: targetY, z: 0 }, 1500).easing(Easing2.Cubic.InOut);
15354
+ tweenGroup.add(posTween);
15355
+ posTween.start();
15356
+ if (mesh.userData.labelObject) {
15357
+ const labelTarget = new THREE3.Vector3(targetX, targetY, 0).add(mesh.userData.labelOffset || new THREE3.Vector3(0, 3.8, 0));
15358
+ const lblTween = new Tween2(mesh.userData.labelObject.position).to(labelTarget, 1500).easing(Easing2.Cubic.InOut);
15359
+ tweenGroup.add(lblTween);
15360
+ lblTween.start();
15361
+ }
15362
+ sumX += targetX;
15363
+ sumY += targetY;
15364
+ nodeCount++;
15365
+ });
15366
+ }
15367
+ if (nodeCount > 0) {
15368
+ const centerX = sumX / nodeCount;
15369
+ const centerY = sumY / nodeCount;
15370
+ const targetFocus = new THREE3.Vector3(centerX, centerY, 0);
15371
+ const targetTween = new Tween2(controls.target).to({ x: targetFocus.x, y: targetFocus.y, z: targetFocus.z }, 1500).easing(Easing2.Cubic.InOut);
15372
+ tweenGroup.add(targetTween);
15373
+ targetTween.start();
15374
+ let zoomZ = 140;
15375
+ if (type === "Column") {
15376
+ const numRows = Math.ceil(nodeCount / (columns || 4));
15377
+ zoomZ = Math.max(140, numRows * 50);
15378
+ } else if (type === "Circle" || type === "Star") {
15379
+ const radius = Math.max(80, nodeCount * 8);
15380
+ zoomZ = radius * 2.5;
15381
+ }
15382
+ const camTween = new Tween2(camera.position).to({ x: targetFocus.x, y: targetFocus.y, z: zoomZ }, 1500).easing(Easing2.Cubic.InOut);
15383
+ tweenGroup.add(camTween);
15384
+ camTween.start();
15385
+ }
15386
+ }, []);
15191
15387
  const handleActivateQuestLens = useCallback6(({ assigneeId, status: status2, milestoneId }) => {
15192
15388
  var _a2;
15193
15389
  const { nodeObjects, tweenGroup, graphGroup } = stateRef.current;
@@ -16079,31 +16275,36 @@ function XViewScene({
16079
16275
  (_b2 = stateRef.current.tweenGroup) == null ? void 0 : _b2.update(time);
16080
16276
  const { keysPressed, controls: sceneControls, camera: sceneCamera } = stateRef.current;
16081
16277
  if (sceneControls && keysPressed) {
16082
- const panSpeed = 0.5;
16278
+ const panSpeed = 1.5;
16083
16279
  const rotateSpeed = 0.02;
16084
16280
  const zoomInFactor = 0.98;
16085
16281
  const zoomOutFactor = 1.02;
16086
16282
  const isShift = !!keysPressed["shiftKey"];
16087
16283
  const isCtrl = !!keysPressed["ctrlKey"];
16088
- if (isCtrl) {
16284
+ const dirX = (keysPressed["ArrowRight"] ? 1 : 0) - (keysPressed["ArrowLeft"] ? 1 : 0);
16285
+ const dirY = (keysPressed["ArrowUp"] ? 1 : 0) - (keysPressed["ArrowDown"] ? 1 : 0);
16286
+ if (isCtrl && (dirX !== 0 || dirY !== 0)) {
16089
16287
  const offset = sceneCamera.position.clone().sub(sceneControls.target);
16090
16288
  const spherical = new THREE3.Spherical().setFromVector3(offset);
16091
- if (keysPressed["ArrowLeft"]) spherical.theta += rotateSpeed;
16092
- if (keysPressed["ArrowRight"]) spherical.theta -= rotateSpeed;
16093
- if (keysPressed["ArrowUp"]) spherical.phi -= rotateSpeed;
16094
- if (keysPressed["ArrowDown"]) spherical.phi += rotateSpeed;
16289
+ spherical.theta += dirX * rotateSpeed;
16290
+ spherical.phi -= dirY * rotateSpeed;
16095
16291
  spherical.makeSafe();
16096
16292
  offset.setFromSpherical(spherical);
16097
16293
  sceneCamera.position.copy(sceneControls.target).add(offset);
16098
- } else if (isShift) {
16099
- if (keysPressed["ArrowUp"]) sceneControls.dollyIn(zoomInFactor);
16100
- if (keysPressed["ArrowDown"]) sceneControls.dollyOut(zoomOutFactor);
16101
- } else {
16102
- const panStep = 10;
16103
- if (keysPressed["ArrowLeft"]) sceneControls.pan(panStep, 0);
16104
- if (keysPressed["ArrowRight"]) sceneControls.pan(-panStep, 0);
16105
- if (keysPressed["ArrowUp"]) sceneControls.pan(0, panStep);
16106
- if (keysPressed["ArrowDown"]) sceneControls.pan(0, -panStep);
16294
+ } else if (isShift && dirY !== 0) {
16295
+ const factor = keysPressed["ArrowUp"] ? zoomInFactor : zoomOutFactor;
16296
+ const offset = sceneCamera.position.clone().sub(sceneControls.target);
16297
+ offset.multiplyScalar(factor);
16298
+ sceneCamera.position.copy(sceneControls.target).add(offset);
16299
+ } else if (!isCtrl && !isShift && (dirX !== 0 || dirY !== 0)) {
16300
+ const distance = sceneCamera.position.distanceTo(sceneControls.target);
16301
+ const currentPanSpeed = panSpeed * (distance / 100);
16302
+ const vecX = new THREE3.Vector3().setFromMatrixColumn(sceneCamera.matrix, 0);
16303
+ const vecY = new THREE3.Vector3().setFromMatrixColumn(sceneCamera.matrix, 1);
16304
+ vecX.multiplyScalar(dirX * currentPanSpeed);
16305
+ vecY.multiplyScalar(dirY * currentPanSpeed);
16306
+ sceneCamera.position.add(vecX).add(vecY);
16307
+ sceneControls.target.add(vecX).add(vecY);
16107
16308
  }
16108
16309
  }
16109
16310
  stateRef.current.controls.update();
@@ -18754,6 +18955,7 @@ function XViewScene({
18754
18955
  importSuccessMessage,
18755
18956
  onActivateTimeline: handleActivateTimeline,
18756
18957
  onActivateQuestLens: handleActivateQuestLens,
18958
+ onActivateReshapeLens: handleActivateReshapeLens,
18757
18959
  onOpenBoards: handleOpenBoards,
18758
18960
  viewType: viewParams == null ? void 0 : viewParams.type,
18759
18961
  viewName: viewParams == null ? void 0 : viewParams.name,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lv-x-software-house/x_view",
3
- "version": "1.2.6-dev.49",
3
+ "version": "1.2.6-dev.51",
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",