@lv-x-software-house/x_view 1.2.5-dev.4 → 1.2.5-dev.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.
- package/dist/index.js +222 -51
- package/dist/index.mjs +222 -51
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -111,6 +111,7 @@ function ContextMenu({
|
|
|
111
111
|
const [menuView, setMenuView] = useState("main");
|
|
112
112
|
const [selectedAncestry, setSelectedAncestry] = useState(null);
|
|
113
113
|
const [versionSubMenu, setVersionSubMenu] = useState(null);
|
|
114
|
+
const [labelSubMenu, setLabelSubMenu] = useState(null);
|
|
114
115
|
const [isLinkCopied, setIsLinkCopied] = useState(false);
|
|
115
116
|
const [selectedQuestStatus, setSelectedQuestStatus] = useState(null);
|
|
116
117
|
const ability = useMemo(() => defineAbilityFor(userRole), [userRole]);
|
|
@@ -119,6 +120,7 @@ function ContextMenu({
|
|
|
119
120
|
setMenuView("main");
|
|
120
121
|
setSelectedAncestry(null);
|
|
121
122
|
setVersionSubMenu(null);
|
|
123
|
+
setLabelSubMenu(null);
|
|
122
124
|
setSelectedQuestStatus(null);
|
|
123
125
|
}
|
|
124
126
|
}, [data.visible, (_a = data.nodeData) == null ? void 0 : _a.id]);
|
|
@@ -134,7 +136,7 @@ function ContextMenu({
|
|
|
134
136
|
if (left + w + 8 > vw) left = Math.max(8, vw - w - 8);
|
|
135
137
|
if (top + h + 8 > vh) top = Math.max(8, vh - h - 8);
|
|
136
138
|
setMenuPos({ left, top });
|
|
137
|
-
}, [data, menuView, versionSubMenu, selectedQuestStatus]);
|
|
139
|
+
}, [data, menuView, versionSubMenu, labelSubMenu, selectedQuestStatus]);
|
|
138
140
|
useEffect(() => {
|
|
139
141
|
if (!data.visible) return;
|
|
140
142
|
const handleClickOutside = (e) => {
|
|
@@ -186,7 +188,21 @@ function ContextMenu({
|
|
|
186
188
|
var _a2;
|
|
187
189
|
return (_a2 = c.targetNode) == null ? void 0 : _a2.is_quest;
|
|
188
190
|
});
|
|
189
|
-
const
|
|
191
|
+
const getLabelForConnection = (conn) => {
|
|
192
|
+
if (conn.direction === "outgoing") return conn.link.source_label || null;
|
|
193
|
+
if (conn.direction === "incoming") return conn.link.target_label || null;
|
|
194
|
+
return null;
|
|
195
|
+
};
|
|
196
|
+
const commonWithLabel = commonConnections.filter((c) => getLabelForConnection(c));
|
|
197
|
+
const commonWithoutLabel = commonConnections.filter((c) => !getLabelForConnection(c));
|
|
198
|
+
const labelGroups = commonWithLabel.reduce((acc, conn) => {
|
|
199
|
+
const label = getLabelForConnection(conn);
|
|
200
|
+
if (!acc[label]) acc[label] = [];
|
|
201
|
+
acc[label].push(conn);
|
|
202
|
+
return acc;
|
|
203
|
+
}, {});
|
|
204
|
+
const labelGroupEntries = Object.entries(labelGroups).sort(([a], [b]) => a.localeCompare(b));
|
|
205
|
+
const groupedConnections = commonWithoutLabel.reduce((acc, conn) => {
|
|
190
206
|
var _a2;
|
|
191
207
|
const { targetNode } = conn;
|
|
192
208
|
const groupingKey = ((_a2 = targetNode.version_node) == null ? void 0 : _a2.is_version) ? targetNode.version_node.parent_node : targetNode.id;
|
|
@@ -314,11 +330,32 @@ function ContextMenu({
|
|
|
314
330
|
/* @__PURE__ */ React.createElement("span", { className: "flex-1 truncate" }, conn.targetNode.name)
|
|
315
331
|
))));
|
|
316
332
|
};
|
|
333
|
+
const renderLabelSubMenuView = () => {
|
|
334
|
+
const group = labelSubMenu;
|
|
335
|
+
const isScrollable = group.connections.length > 10;
|
|
336
|
+
return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between gap-2 px-2 pt-1 pb-2" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-2 min-w-0" }, /* @__PURE__ */ React.createElement("button", { onClick: () => {
|
|
337
|
+
setLabelSubMenu(null);
|
|
338
|
+
setMenuView("connections");
|
|
339
|
+
}, className: "p-1 rounded-full hover:bg-white/10 text-slate-400 hover:text-white flex-shrink-0" }, /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("polyline", { points: "15 18 9 12 15 6" }))), /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-1.5 min-w-0" }, /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "text-violet-400 flex-shrink-0" }, /* @__PURE__ */ React.createElement("path", { d: "M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z" }), /* @__PURE__ */ React.createElement("line", { x1: "7", y1: "7", x2: "7.01", y2: "7" })), /* @__PURE__ */ React.createElement("p", { className: "text-[11px] uppercase tracking-wider text-violet-300/80 truncate", title: group.label }, group.label))), group.connections.length > 0 && /* @__PURE__ */ React.createElement("button", { onClick: () => handleExpandAndClose(group.connections.map((c) => c.link)), className: "px-2 py-0.5 text-xs bg-indigo-500/50 hover:bg-indigo-500/80 rounded-md transition-colors" }, "Expandir Todas")), /* @__PURE__ */ React.createElement("div", { className: `flex flex-col gap-1 ${isScrollable ? "max-h-[40vh] overflow-y-auto custom-scrollbar" : ""}` }, group.connections.map((conn) => /* @__PURE__ */ React.createElement(
|
|
340
|
+
"button",
|
|
341
|
+
{
|
|
342
|
+
key: conn.targetNode.id,
|
|
343
|
+
onClick: () => handleExpandAndClose([conn.link]),
|
|
344
|
+
className: baseButtonClass,
|
|
345
|
+
title: `Expandir conex\xE3o com ${conn.targetNode.name}`
|
|
346
|
+
},
|
|
347
|
+
/* @__PURE__ */ React.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__ */ React.createElement("line", { x1: "5", y1: "12", x2: "19", y2: "12" }), /* @__PURE__ */ React.createElement("polyline", { points: "12 5 19 12 12 19" })),
|
|
348
|
+
/* @__PURE__ */ React.createElement("span", { className: "flex-1 truncate" }, conn.targetNode.name)
|
|
349
|
+
))));
|
|
350
|
+
};
|
|
317
351
|
const renderConnectionsView = () => {
|
|
318
352
|
if (versionSubMenu) {
|
|
319
353
|
return renderVersionSubMenuView();
|
|
320
354
|
}
|
|
321
|
-
|
|
355
|
+
if (labelSubMenu) {
|
|
356
|
+
return renderLabelSubMenuView();
|
|
357
|
+
}
|
|
358
|
+
const totalItems = availableAncestries.length + labelGroupEntries.length + finalRenderableConnections.length + (questConnections.length > 0 ? 1 : 0);
|
|
322
359
|
const isScrollable = totalItems > 10;
|
|
323
360
|
return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between gap-2 px-2 pt-1 pb-2" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React.createElement("button", { onClick: () => setMenuView("main"), className: "p-1 rounded-full hover:bg-white/10 text-slate-400 hover:text-white" }, /* @__PURE__ */ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("polyline", { points: "15 18 9 12 15 6" }))), /* @__PURE__ */ React.createElement("p", { className: "text-[11px] uppercase tracking-wider text-slate-400" }, "Conex\xF5es")), commonConnections.length > 0 && /* @__PURE__ */ React.createElement("button", { onClick: () => handleExpandAndClose(commonConnections.map((c) => c.link)), className: "px-2 py-0.5 text-xs bg-indigo-500/50 hover:bg-indigo-500/80 rounded-md transition-colors" }, "Expandir Todas")), /* @__PURE__ */ React.createElement("div", { className: `flex flex-col ${isScrollable ? "max-h-[40vh] overflow-y-auto custom-scrollbar" : ""}` }, availableAncestries.length > 0 && /* @__PURE__ */ React.createElement("div", { className: `flex flex-col gap-1 ${finalRenderableConnections.length > 0 || questConnections.length > 0 ? "mb-2" : ""}` }, /* @__PURE__ */ React.createElement("div", { className: "px-2 py-1 text-[11px] uppercase tracking-wider text-indigo-400/90" }, "Ancestralidades Salvas"), availableAncestries.map((anc) => /* @__PURE__ */ React.createElement(
|
|
324
361
|
"button",
|
|
@@ -330,7 +367,20 @@ function ContextMenu({
|
|
|
330
367
|
},
|
|
331
368
|
/* @__PURE__ */ React.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__ */ React.createElement("path", { d: "M6 3v4a2 2 0 0 0 2 2h4" }), /* @__PURE__ */ React.createElement("path", { d: "M18 3v4a2 2 0 0 1-2 2h-4" }), /* @__PURE__ */ React.createElement("circle", { cx: "6", cy: "3", r: "2" }), /* @__PURE__ */ React.createElement("circle", { cx: "18", cy: "3", r: "2" }), /* @__PURE__ */ React.createElement("circle", { cx: "12", cy: "11", r: "2" }), /* @__PURE__ */ React.createElement("path", { d: "M12 13v6" }), /* @__PURE__ */ React.createElement("circle", { cx: "12", cy: "21", r: "2" })),
|
|
332
369
|
/* @__PURE__ */ React.createElement("span", { className: "flex-1 truncate" }, anc.name || `Ancestralidade #${anc.ancestry_id.substring(0, 8)}`)
|
|
333
|
-
))),
|
|
370
|
+
))), labelGroupEntries.length > 0 && /* @__PURE__ */ React.createElement("div", { className: `flex flex-col gap-1 ${availableAncestries.length > 0 ? "mt-2" : ""} ${finalRenderableConnections.length > 0 || questConnections.length > 0 ? "mb-2" : ""}` }, availableAncestries.length > 0 && /* @__PURE__ */ React.createElement("div", { className: "my-1 h-px w-full bg-white/10" }), labelGroupEntries.map(([label, conns]) => /* @__PURE__ */ React.createElement(
|
|
371
|
+
"button",
|
|
372
|
+
{
|
|
373
|
+
key: label,
|
|
374
|
+
onClick: () => {
|
|
375
|
+
setLabelSubMenu({ label, connections: conns });
|
|
376
|
+
},
|
|
377
|
+
className: baseButtonClass,
|
|
378
|
+
title: `Ver grupo: ${label}`
|
|
379
|
+
},
|
|
380
|
+
/* @__PURE__ */ React.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", className: "text-violet-400" }, /* @__PURE__ */ React.createElement("path", { d: "M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z" }), /* @__PURE__ */ React.createElement("line", { x1: "7", y1: "7", x2: "7.01", y2: "7" })),
|
|
381
|
+
/* @__PURE__ */ React.createElement("span", { className: "flex-1 truncate" }, label),
|
|
382
|
+
/* @__PURE__ */ React.createElement("span", { className: "text-xs px-2 py-0.5 bg-violet-500/20 text-violet-300 rounded-full" }, conns.length)
|
|
383
|
+
))), finalRenderableConnections.length > 0 && /* @__PURE__ */ React.createElement("div", { className: "flex flex-col gap-1" }, (availableAncestries.length > 0 || labelGroupEntries.length > 0) && /* @__PURE__ */ React.createElement("div", { className: "my-1 h-px w-full bg-white/10" }), finalRenderableConnections.map((group) => {
|
|
334
384
|
if (group.isVersionGroup) {
|
|
335
385
|
return /* @__PURE__ */ React.createElement(
|
|
336
386
|
"button",
|
|
@@ -666,7 +716,7 @@ function XViewSidebar({
|
|
|
666
716
|
"div",
|
|
667
717
|
{
|
|
668
718
|
ref: containerRef,
|
|
669
|
-
className: "ui-overlay fixed left-0 top-0 h-
|
|
719
|
+
className: "ui-overlay fixed left-0 top-0 h-[100dvh] w-[min(92vw,320px)] z-40 overflow-hidden",
|
|
670
720
|
onPointerDown: swallow,
|
|
671
721
|
onPointerMove: swallow,
|
|
672
722
|
onPointerUp: swallow,
|
|
@@ -675,7 +725,7 @@ function XViewSidebar({
|
|
|
675
725
|
onContextMenu: swallow,
|
|
676
726
|
onDoubleClick: swallow
|
|
677
727
|
},
|
|
678
|
-
/* @__PURE__ */ React2.createElement("div", { className: "h-full flex flex-col bg-slate-950/80 backdrop-blur-xl border-r border-white/10 shadow-[0_0_40px_rgba(0,0,0,0.45)]" }, /* @__PURE__ */ React2.createElement("div", { className: "relative px-4 py-3 border-b border-white/10 flex items-center gap-2" }, /* @__PURE__ */ React2.createElement("span", { className: "inline-flex h-2 w-2 rounded-full bg-indigo-400/80 shadow-[0_0_10px_2px_rgba(99,102,241,0.55)]" }), /* @__PURE__ */ React2.createElement("h3", { className: "text-sm font-medium text-slate-100" }, "Ferramentas"), /* @__PURE__ */ React2.createElement(
|
|
728
|
+
/* @__PURE__ */ React2.createElement("div", { className: "h-full flex flex-col overflow-hidden bg-slate-950/80 backdrop-blur-xl border-r border-white/10 shadow-[0_0_40px_rgba(0,0,0,0.45)]" }, /* @__PURE__ */ React2.createElement("div", { className: "relative px-4 py-3 border-b border-white/10 flex items-center gap-2" }, /* @__PURE__ */ React2.createElement("span", { className: "inline-flex h-2 w-2 rounded-full bg-indigo-400/80 shadow-[0_0_10px_2px_rgba(99,102,241,0.55)]" }), /* @__PURE__ */ React2.createElement("h3", { className: "text-sm font-medium text-slate-100" }, "Ferramentas"), /* @__PURE__ */ React2.createElement(
|
|
679
729
|
"button",
|
|
680
730
|
{
|
|
681
731
|
className: "ml-auto p-2 rounded-md text-slate-400 hover:text-white hover:bg-white/10 transition-colors",
|
|
@@ -732,7 +782,7 @@ function XViewSidebar({
|
|
|
732
782
|
autoComplete: "off"
|
|
733
783
|
}
|
|
734
784
|
)
|
|
735
|
-
), showList && /* @__PURE__ */ React2.createElement("ul", { className: "custom-scrollbar absolute mt-1 z-10 w-full max-h-[
|
|
785
|
+
), showList && /* @__PURE__ */ React2.createElement("ul", { className: "custom-scrollbar absolute mt-1 z-10 w-full max-h-[min(448px,50dvh)] overflow-y-auto rounded-lg bg-slate-900/95 border border-white/10 shadow-xl" }, filteredAndSorted.length > 0 ? filteredAndSorted.map((n) => {
|
|
736
786
|
const inView = isNodeInView(n.id);
|
|
737
787
|
const active = selectedNodeId === n.id;
|
|
738
788
|
const typeLabel = Array.isArray(n.type) ? n.type.join(", ") : n.type;
|
|
@@ -3108,6 +3158,7 @@ function calculateNodePositions(nodes) {
|
|
|
3108
3158
|
return positions;
|
|
3109
3159
|
}
|
|
3110
3160
|
function updateTooltip({ tooltipEl, hoveredNode, hoveredLink, camera, mountEl, isSceneBusy, parentData, ancestryData }) {
|
|
3161
|
+
var _a, _b;
|
|
3111
3162
|
if (!tooltipEl || !camera || !mountEl) return;
|
|
3112
3163
|
let content = "";
|
|
3113
3164
|
let positionTarget = null;
|
|
@@ -3120,17 +3171,21 @@ function updateTooltip({ tooltipEl, hoveredNode, hoveredLink, camera, mountEl, i
|
|
|
3120
3171
|
content = generateTooltipHtml(hoveredNode.userData, parentData, ancestryData);
|
|
3121
3172
|
}
|
|
3122
3173
|
} else if (hoveredLink && !isSceneBusy) {
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3174
|
+
const linkData = hoveredLink.userData.isAncestryLink ? hoveredLink.userData.relationship || {} : hoveredLink.userData;
|
|
3175
|
+
const hasContent = ((_a = linkData.name) == null ? void 0 : _a.trim()) || ((_b = linkData.description) == null ? void 0 : _b.trim());
|
|
3176
|
+
if (hasContent) {
|
|
3177
|
+
currentId = `link_${hoveredLink.userData.id}`;
|
|
3178
|
+
if (hoveredLink.userData.isCurved) {
|
|
3179
|
+
const positions = hoveredLink.geometry.attributes.position.array;
|
|
3180
|
+
const midIndex = Math.floor(positions.length / 2 / 3) * 3;
|
|
3181
|
+
positionTarget = new THREE2.Vector3(positions[midIndex], positions[midIndex + 1], positions[midIndex + 2]);
|
|
3182
|
+
} else {
|
|
3183
|
+
positionTarget = new THREE2.Vector3().addVectors(hoveredLink.userData.sourceNode.position, hoveredLink.userData.targetNode.position).multiplyScalar(0.5);
|
|
3184
|
+
}
|
|
3185
|
+
isLink = true;
|
|
3186
|
+
if (tooltipEl.dataset.currentId !== currentId) {
|
|
3187
|
+
content = generateLinkTooltipHtml(linkData, parentData, ancestryData);
|
|
3188
|
+
}
|
|
3134
3189
|
}
|
|
3135
3190
|
}
|
|
3136
3191
|
if (positionTarget) {
|
|
@@ -3439,9 +3494,9 @@ function CustomPropertyDisplay({
|
|
|
3439
3494
|
};
|
|
3440
3495
|
const handleRemoveListItem = (j) => setTempProp((p) => ({ ...p, value: p.value.filter((_, k) => k !== j) }));
|
|
3441
3496
|
const handleListItemChange = (j, f, v) => setTempProp((p) => {
|
|
3442
|
-
const
|
|
3443
|
-
|
|
3444
|
-
return { ...p, value:
|
|
3497
|
+
const newValue2 = [...p.value];
|
|
3498
|
+
newValue2[j] = { ...newValue2[j], [f]: v };
|
|
3499
|
+
return { ...p, value: newValue2 };
|
|
3445
3500
|
});
|
|
3446
3501
|
const baseInput = "w-full bg-slate-800/70 p-2 text-sm rounded-lg border border-white/10 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-indigo-400/60 transition-all duration-150";
|
|
3447
3502
|
const renderEditView = () => {
|
|
@@ -3495,14 +3550,14 @@ function CustomPropertyDisplay({
|
|
|
3495
3550
|
const inputClass = `${baseInput} ${noSpinnerClass}`;
|
|
3496
3551
|
const handleDateTypeChange = (newDateType) => {
|
|
3497
3552
|
var _a3, _b2, _c2;
|
|
3498
|
-
let
|
|
3553
|
+
let newValue2 = { type: newDateType };
|
|
3499
3554
|
if (newDateType === "Date Interval") {
|
|
3500
|
-
|
|
3501
|
-
|
|
3555
|
+
newValue2.start = ((_a3 = tempProp.value) == null ? void 0 : _a3.start) || "";
|
|
3556
|
+
newValue2.end = ((_b2 = tempProp.value) == null ? void 0 : _b2.end) || "";
|
|
3502
3557
|
} else {
|
|
3503
|
-
|
|
3558
|
+
newValue2.value = ((_c2 = tempProp.value) == null ? void 0 : _c2.type) === newDateType ? tempProp.value.value : "";
|
|
3504
3559
|
}
|
|
3505
|
-
handlePropChange("value",
|
|
3560
|
+
handlePropChange("value", newValue2);
|
|
3506
3561
|
};
|
|
3507
3562
|
const handleDateValueChange = (dateField, dateValue) => {
|
|
3508
3563
|
handlePropChange("value", { ...tempProp.value, [dateField]: dateValue });
|
|
@@ -5409,7 +5464,7 @@ function AncestryRelationshipPanel({
|
|
|
5409
5464
|
}, 100);
|
|
5410
5465
|
};
|
|
5411
5466
|
const handleRemoveProp = (i) => setCustomProps((p) => p.filter((_, idx) => idx !== i));
|
|
5412
|
-
const
|
|
5467
|
+
const handleUpdateProp2 = (index, updatedProp) => {
|
|
5413
5468
|
setCustomProps((props) => {
|
|
5414
5469
|
const newProps = [...props];
|
|
5415
5470
|
newProps[index] = updatedProp;
|
|
@@ -5521,7 +5576,7 @@ function AncestryRelationshipPanel({
|
|
|
5521
5576
|
{
|
|
5522
5577
|
key: prop.id,
|
|
5523
5578
|
prop,
|
|
5524
|
-
onUpdate: (updatedProp) =>
|
|
5579
|
+
onUpdate: (updatedProp) => handleUpdateProp2(idx, updatedProp),
|
|
5525
5580
|
onRemove: () => handleRemoveProp(idx),
|
|
5526
5581
|
onOpenImageViewer,
|
|
5527
5582
|
unavailableTypes: currentUsedTypes.filter((t) => t !== prop.type),
|
|
@@ -5731,6 +5786,7 @@ var NodeItem = ({ nodeData, onSelectParent, onViewSelect, highlightedPathIds = [
|
|
|
5731
5786
|
e.stopPropagation();
|
|
5732
5787
|
if (isEditable) {
|
|
5733
5788
|
onSelectParent(itemId);
|
|
5789
|
+
setHasUnsavedChanges(true);
|
|
5734
5790
|
} else if (onViewSelect) {
|
|
5735
5791
|
onViewSelect(itemId);
|
|
5736
5792
|
}
|
|
@@ -5743,9 +5799,11 @@ var NodeItem = ({ nodeData, onSelectParent, onViewSelect, highlightedPathIds = [
|
|
|
5743
5799
|
level > 0 && isEditable && /* @__PURE__ */ React10.createElement("div", { className: "flex items-center gap-1 animate-in fade-in duration-200" }, !nodeData.is_section && /* @__PURE__ */ React10.createElement("button", { onClick: (e) => {
|
|
5744
5800
|
e.stopPropagation();
|
|
5745
5801
|
onEditRelationship(path, nodeData.relationship || {});
|
|
5802
|
+
setHasUnsavedChanges(true);
|
|
5746
5803
|
}, className: "w-6 h-6 grid place-content-center rounded-full hover:bg-cyan-500/20 text-cyan-400 transition-colors flex-shrink-0", title: "Editar detalhes da rela\xE7\xE3o" }, /* @__PURE__ */ React10.createElement(FiEdit23, { size: 12 })), /* @__PURE__ */ React10.createElement("button", { onClick: (e) => {
|
|
5747
5804
|
e.stopPropagation();
|
|
5748
5805
|
onRemoveNode(path);
|
|
5806
|
+
setHasUnsavedChanges(true);
|
|
5749
5807
|
}, className: "w-6 h-6 grid place-content-center rounded-full hover:bg-red-500/20 text-red-400 text-lg transition-colors flex-shrink-0", title: "Remover Node" }, "\xD7"))
|
|
5750
5808
|
), hasChildren && /* @__PURE__ */ React10.createElement("div", { className: "mt-2 space-y-2 pl-8" }, nodeData.children.map((child, index) => /* @__PURE__ */ React10.createElement(
|
|
5751
5809
|
NodeItem,
|
|
@@ -5801,6 +5859,7 @@ function CreateAncestryPanel({
|
|
|
5801
5859
|
} = ancestryMode;
|
|
5802
5860
|
const [isSaving, setIsSaving] = useState11(false);
|
|
5803
5861
|
const [isLinkCopied, setIsLinkCopied] = useState11(false);
|
|
5862
|
+
const [hasUnsavedChanges, setHasUnsavedChanges2] = useState11(false);
|
|
5804
5863
|
const [showDeleteBranchConfirm, setShowDeleteBranchConfirm] = useState11(false);
|
|
5805
5864
|
const handleCopyLink = (e) => {
|
|
5806
5865
|
e.stopPropagation();
|
|
@@ -5868,12 +5927,14 @@ function CreateAncestryPanel({
|
|
|
5868
5927
|
};
|
|
5869
5928
|
const handleSelectAncestryParent = (nodeId, isAbstraction = false) => {
|
|
5870
5929
|
setAncestryMode((prev) => isAbstraction ? { ...prev, selectedAbstractionParentId: nodeId } : { ...prev, selectedParentId: nodeId });
|
|
5930
|
+
setHasUnsavedChanges2(true);
|
|
5871
5931
|
};
|
|
5872
5932
|
const handleToggleAddMode = (isAbstraction = false) => {
|
|
5873
5933
|
if (isAbstraction && !ancestryMode.isAddingAbstractionNodes) {
|
|
5874
5934
|
setTargetRenderNodeId(null);
|
|
5875
5935
|
}
|
|
5876
5936
|
setAncestryMode((prev) => isAbstraction ? { ...prev, isAddingAbstractionNodes: !prev.isAddingAbstractionNodes } : { ...prev, isAddingNodes: !prev.isAddingNodes });
|
|
5937
|
+
setHasUnsavedChanges2(true);
|
|
5877
5938
|
};
|
|
5878
5939
|
const handleRemoveNode = useCallback2((pathToRemove, isAbstraction = false) => {
|
|
5879
5940
|
if (!Array.isArray(pathToRemove) || pathToRemove.length === 0) return;
|
|
@@ -5891,6 +5952,7 @@ function CreateAncestryPanel({
|
|
|
5891
5952
|
const indexToRemove = pathToRemove[pathToRemove.length - 1];
|
|
5892
5953
|
if (currentParent.children && currentParent.children.length > indexToRemove) {
|
|
5893
5954
|
currentParent.children.splice(indexToRemove, 1);
|
|
5955
|
+
setHasUnsavedChanges2(true);
|
|
5894
5956
|
}
|
|
5895
5957
|
return { ...prev, [treeKey]: newTree };
|
|
5896
5958
|
});
|
|
@@ -5949,6 +6011,7 @@ function CreateAncestryPanel({
|
|
|
5949
6011
|
updateGlobalTree(rootTreeClone);
|
|
5950
6012
|
}
|
|
5951
6013
|
setAncestryMode((prev) => ({ ...prev, [treeKey]: rootTreeClone }));
|
|
6014
|
+
setHasUnsavedChanges2(true);
|
|
5952
6015
|
} else {
|
|
5953
6016
|
alert("N\xE3o \xE9 poss\xEDvel mover um node para dentro de seus pr\xF3prios descendentes.");
|
|
5954
6017
|
}
|
|
@@ -6021,6 +6084,7 @@ function CreateAncestryPanel({
|
|
|
6021
6084
|
const handleAddProp = () => {
|
|
6022
6085
|
const newProp = createNewCustomProperty(customProps);
|
|
6023
6086
|
setCustomProps((p) => [...p, newProp]);
|
|
6087
|
+
setHasUnsavedChanges2(true);
|
|
6024
6088
|
setTimeout(() => {
|
|
6025
6089
|
var _a;
|
|
6026
6090
|
(_a = propsEndRef.current) == null ? void 0 : _a.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
@@ -6029,11 +6093,13 @@ function CreateAncestryPanel({
|
|
|
6029
6093
|
const handleRemoveProp = (i) => {
|
|
6030
6094
|
const newProps = customProps.filter((_, idx) => idx !== i);
|
|
6031
6095
|
setCustomProps(newProps);
|
|
6096
|
+
setHasUnsavedChanges2(true);
|
|
6032
6097
|
};
|
|
6033
|
-
const
|
|
6098
|
+
const handleUpdateProp2 = (index, updatedProp) => {
|
|
6034
6099
|
const newProps = [...customProps];
|
|
6035
6100
|
newProps[index] = updatedProp;
|
|
6036
6101
|
setCustomProps(newProps);
|
|
6102
|
+
setHasUnsavedChanges2(true);
|
|
6037
6103
|
};
|
|
6038
6104
|
const currentUsedTypes = customProps.map((p) => p.type).filter((t) => UNIQUE_PROP_TYPES.includes(t));
|
|
6039
6105
|
useEffect10(() => {
|
|
@@ -6143,6 +6209,7 @@ function CreateAncestryPanel({
|
|
|
6143
6209
|
updateGlobalTree(rootTreeClone);
|
|
6144
6210
|
setBranchStack([...branchStack]);
|
|
6145
6211
|
setIsPickerOpen(false);
|
|
6212
|
+
setHasUnsavedChanges2(true);
|
|
6146
6213
|
try {
|
|
6147
6214
|
setIsSaving(true);
|
|
6148
6215
|
const rootProps = extractCustomPropsFromNode(ancestryMode);
|
|
@@ -6156,6 +6223,7 @@ function CreateAncestryPanel({
|
|
|
6156
6223
|
rootExtras
|
|
6157
6224
|
);
|
|
6158
6225
|
setLastSavedSnapshot(takeSnapshot(rootTreeClone, ancestryName, description, processedSections, [], isPrivate, ancestryMode.abstraction_tree));
|
|
6226
|
+
setHasUnsavedChanges2(false);
|
|
6159
6227
|
if (onRenderFullAncestry) {
|
|
6160
6228
|
const fullTreePayload = {
|
|
6161
6229
|
ancestry_id: ancestryMode.currentAncestryId || "temp_root",
|
|
@@ -6198,6 +6266,7 @@ function CreateAncestryPanel({
|
|
|
6198
6266
|
if (branchIndex !== -1) {
|
|
6199
6267
|
foundParentPath.node.parallel_branches.splice(branchIndex, 1);
|
|
6200
6268
|
updateGlobalTree(rootTreeClone);
|
|
6269
|
+
setHasUnsavedChanges2(true);
|
|
6201
6270
|
try {
|
|
6202
6271
|
setIsSaving(true);
|
|
6203
6272
|
const currentRootProps = extractCustomPropsFromNode(ancestryMode);
|
|
@@ -6219,6 +6288,7 @@ function CreateAncestryPanel({
|
|
|
6219
6288
|
isPrivate,
|
|
6220
6289
|
ancestryMode.abstraction_tree
|
|
6221
6290
|
));
|
|
6291
|
+
setHasUnsavedChanges2(false);
|
|
6222
6292
|
if (onClearAncestryVisuals) {
|
|
6223
6293
|
onClearAncestryVisuals(currentStep.branchId);
|
|
6224
6294
|
}
|
|
@@ -6251,6 +6321,7 @@ function CreateAncestryPanel({
|
|
|
6251
6321
|
if (branchIndex !== -1) {
|
|
6252
6322
|
foundParentPath.node.parallel_branches.splice(branchIndex, 1);
|
|
6253
6323
|
updateGlobalTree(rootTreeClone);
|
|
6324
|
+
setHasUnsavedChanges2(true);
|
|
6254
6325
|
try {
|
|
6255
6326
|
setIsSaving(true);
|
|
6256
6327
|
const currentRootProps = extractCustomPropsFromNode(ancestryMode);
|
|
@@ -6272,6 +6343,7 @@ function CreateAncestryPanel({
|
|
|
6272
6343
|
isPrivate,
|
|
6273
6344
|
ancestryMode.abstraction_tree
|
|
6274
6345
|
));
|
|
6346
|
+
setHasUnsavedChanges2(false);
|
|
6275
6347
|
if (onClearAncestryVisuals) {
|
|
6276
6348
|
onClearAncestryVisuals(currentStep.branchId);
|
|
6277
6349
|
}
|
|
@@ -6533,6 +6605,7 @@ function CreateAncestryPanel({
|
|
|
6533
6605
|
}
|
|
6534
6606
|
setBranchStack(parentStack);
|
|
6535
6607
|
setTargetScrollSectionId(targetFocusId);
|
|
6608
|
+
setHasUnsavedChanges2(true);
|
|
6536
6609
|
if (onRenderFullAncestry) {
|
|
6537
6610
|
const parentStack2 = currentStack;
|
|
6538
6611
|
const rotation = parentStack2.reduce((acc, step) => {
|
|
@@ -6594,7 +6667,6 @@ function CreateAncestryPanel({
|
|
|
6594
6667
|
direction,
|
|
6595
6668
|
tree: {
|
|
6596
6669
|
node: nodeData,
|
|
6597
|
-
node_id: nodeId,
|
|
6598
6670
|
children: [],
|
|
6599
6671
|
relationship: {}
|
|
6600
6672
|
}
|
|
@@ -6616,6 +6688,7 @@ function CreateAncestryPanel({
|
|
|
6616
6688
|
savedMaxIndex: parentIndexToSave,
|
|
6617
6689
|
entryDirection: direction
|
|
6618
6690
|
}]);
|
|
6691
|
+
setHasUnsavedChanges2(true);
|
|
6619
6692
|
if (branch && branch.tree && onRenderFullAncestry) {
|
|
6620
6693
|
const branchAncestryObj = {
|
|
6621
6694
|
ancestry_id: branch.id,
|
|
@@ -6666,6 +6739,10 @@ function CreateAncestryPanel({
|
|
|
6666
6739
|
const currentInputName = overrides.ancestryName !== void 0 ? overrides.ancestryName : ancestryName;
|
|
6667
6740
|
const currentInputDesc = overrides.description !== void 0 ? overrides.description : description;
|
|
6668
6741
|
const currentInputSections = overrides.existingSections !== void 0 ? overrides.existingSections : existingSections;
|
|
6742
|
+
if (!keepOpen && !hasUnsavedChanges) {
|
|
6743
|
+
onClose();
|
|
6744
|
+
return;
|
|
6745
|
+
}
|
|
6669
6746
|
if (!currentInputName.trim()) {
|
|
6670
6747
|
alert("O nome n\xE3o pode estar vazio.");
|
|
6671
6748
|
return;
|
|
@@ -6719,6 +6796,7 @@ function CreateAncestryPanel({
|
|
|
6719
6796
|
isPrivate,
|
|
6720
6797
|
ancestryMode.abstraction_tree
|
|
6721
6798
|
));
|
|
6799
|
+
setHasUnsavedChanges2(false);
|
|
6722
6800
|
if (onRenderFullAncestry) {
|
|
6723
6801
|
const rotation = branchStack.reduce((acc, step) => {
|
|
6724
6802
|
return acc + (step.entryDirection === "left" ? -Math.PI / 2 : Math.PI / 2);
|
|
@@ -6770,6 +6848,7 @@ function CreateAncestryPanel({
|
|
|
6770
6848
|
updatedRootTree,
|
|
6771
6849
|
extrasObj
|
|
6772
6850
|
);
|
|
6851
|
+
setHasUnsavedChanges2(false);
|
|
6773
6852
|
setLastSavedSnapshot(takeSnapshot(
|
|
6774
6853
|
updatedRootTree,
|
|
6775
6854
|
currentInputName,
|
|
@@ -6792,6 +6871,7 @@ function CreateAncestryPanel({
|
|
|
6792
6871
|
const newTreeString = JSON.stringify(newRootTree);
|
|
6793
6872
|
if (currentTreeString !== newTreeString) {
|
|
6794
6873
|
updateGlobalTree(newRootTree);
|
|
6874
|
+
setHasUnsavedChanges2(true);
|
|
6795
6875
|
}
|
|
6796
6876
|
}, [description, existingSections]);
|
|
6797
6877
|
const handleTriggerFullRender = () => {
|
|
@@ -6814,6 +6894,7 @@ function CreateAncestryPanel({
|
|
|
6814
6894
|
};
|
|
6815
6895
|
const handleSaveDescriptionInline = (newDesc) => {
|
|
6816
6896
|
setDescription(newDesc);
|
|
6897
|
+
setHasUnsavedChanges2(true);
|
|
6817
6898
|
handleLocalSave(true, { description: newDesc });
|
|
6818
6899
|
};
|
|
6819
6900
|
const swallow = (e) => e.stopPropagation();
|
|
@@ -6943,7 +7024,11 @@ function CreateAncestryPanel({
|
|
|
6943
7024
|
{
|
|
6944
7025
|
type: "text",
|
|
6945
7026
|
value: ancestryName,
|
|
6946
|
-
onChange: (e) =>
|
|
7027
|
+
onChange: (e) => {
|
|
7028
|
+
setAncestryName(e.target.value);
|
|
7029
|
+
setHasUnsavedChanges2(true);
|
|
7030
|
+
},
|
|
7031
|
+
readOnly: isContextLinked,
|
|
6947
7032
|
placeholder: "Nome da Ancestralidade",
|
|
6948
7033
|
className: "text-xl sm:text-2xl font-semibold tracking-tight bg-transparent border-none p-0 focus:ring-2 focus:ring-indigo-500 rounded-md -ml-1.5 px-1.5 w-full outline-none transition-all focus:bg-slate-800/70"
|
|
6949
7034
|
}
|
|
@@ -7080,7 +7165,7 @@ function CreateAncestryPanel({
|
|
|
7080
7165
|
{
|
|
7081
7166
|
key: prop.id,
|
|
7082
7167
|
prop,
|
|
7083
|
-
onUpdate: (updatedProp) =>
|
|
7168
|
+
onUpdate: (updatedProp) => handleUpdateProp2(idx, updatedProp),
|
|
7084
7169
|
onRemove: () => handleRemoveProp(idx),
|
|
7085
7170
|
unavailableTypes: currentUsedTypes.filter((t) => t !== prop.type),
|
|
7086
7171
|
onUploadFile
|
|
@@ -7562,7 +7647,7 @@ function InSceneCreationForm({
|
|
|
7562
7647
|
}, 100);
|
|
7563
7648
|
};
|
|
7564
7649
|
const handleRemoveProp = (index) => setCustomProps(customProps.filter((_, i) => i !== index));
|
|
7565
|
-
const
|
|
7650
|
+
const handleUpdateProp2 = (index, updatedProp) => {
|
|
7566
7651
|
const newProps = [...customProps];
|
|
7567
7652
|
newProps[index] = updatedProp;
|
|
7568
7653
|
setCustomProps(newProps);
|
|
@@ -7592,9 +7677,9 @@ function InSceneCreationForm({
|
|
|
7592
7677
|
};
|
|
7593
7678
|
const handleToggleImageMode = () => {
|
|
7594
7679
|
var _a2, _b;
|
|
7595
|
-
const
|
|
7596
|
-
setUseImageAsTexture(
|
|
7597
|
-
if (
|
|
7680
|
+
const newValue2 = !useImageAsTexture;
|
|
7681
|
+
setUseImageAsTexture(newValue2);
|
|
7682
|
+
if (newValue2) {
|
|
7598
7683
|
const firstImageProp = customProps.find((p) => p.type === "images");
|
|
7599
7684
|
if (firstImageProp && ((_b = (_a2 = firstImageProp.value) == null ? void 0 : _a2[0]) == null ? void 0 : _b.value)) {
|
|
7600
7685
|
const url = firstImageProp.value[0].value;
|
|
@@ -7800,7 +7885,7 @@ function InSceneCreationForm({
|
|
|
7800
7885
|
{
|
|
7801
7886
|
key: prop.id,
|
|
7802
7887
|
prop,
|
|
7803
|
-
onUpdate: (updatedProp) =>
|
|
7888
|
+
onUpdate: (updatedProp) => handleUpdateProp2(index, updatedProp),
|
|
7804
7889
|
onRemove: () => handleRemoveProp(index),
|
|
7805
7890
|
onOpenImageViewer,
|
|
7806
7891
|
unavailableTypes: currentUsedTypes.filter((t) => t !== prop.type),
|
|
@@ -7881,7 +7966,7 @@ function InSceneVersionForm({
|
|
|
7881
7966
|
}
|
|
7882
7967
|
setCustomProps(customProps.filter((_, i) => i !== index));
|
|
7883
7968
|
};
|
|
7884
|
-
const
|
|
7969
|
+
const handleUpdateProp2 = (index, updatedProp) => {
|
|
7885
7970
|
const newProps = [...customProps];
|
|
7886
7971
|
newProps[index] = updatedProp;
|
|
7887
7972
|
setCustomProps(newProps);
|
|
@@ -7892,9 +7977,9 @@ function InSceneVersionForm({
|
|
|
7892
7977
|
};
|
|
7893
7978
|
const handleToggleImageMode = () => {
|
|
7894
7979
|
var _a, _b;
|
|
7895
|
-
const
|
|
7896
|
-
setUseImageAsTexture(
|
|
7897
|
-
if (
|
|
7980
|
+
const newValue2 = !useImageAsTexture;
|
|
7981
|
+
setUseImageAsTexture(newValue2);
|
|
7982
|
+
if (newValue2) {
|
|
7898
7983
|
const firstImageProp = customProps.find((p) => p.type === "images");
|
|
7899
7984
|
if (firstImageProp && ((_b = (_a = firstImageProp.value) == null ? void 0 : _a[0]) == null ? void 0 : _b.value)) {
|
|
7900
7985
|
const url = firstImageProp.value[0].value;
|
|
@@ -8019,7 +8104,7 @@ function InSceneVersionForm({
|
|
|
8019
8104
|
{
|
|
8020
8105
|
key: prop.id,
|
|
8021
8106
|
prop,
|
|
8022
|
-
onUpdate: (updatedProp) =>
|
|
8107
|
+
onUpdate: (updatedProp) => handleUpdateProp2(index, updatedProp),
|
|
8023
8108
|
onRemove: () => handleRemoveProp(index),
|
|
8024
8109
|
onOpenImageViewer,
|
|
8025
8110
|
unavailableTypes: currentUsedTypes.filter((t) => t !== prop.type),
|
|
@@ -8094,7 +8179,7 @@ function InSceneQuestForm({
|
|
|
8094
8179
|
}, 100);
|
|
8095
8180
|
};
|
|
8096
8181
|
const handleRemoveProp = (index) => setCustomProps(customProps.filter((_, i) => i !== index));
|
|
8097
|
-
const
|
|
8182
|
+
const handleUpdateProp2 = (index, updatedProp) => {
|
|
8098
8183
|
const newProps = [...customProps];
|
|
8099
8184
|
newProps[index] = updatedProp;
|
|
8100
8185
|
setCustomProps(newProps);
|
|
@@ -8248,7 +8333,7 @@ function InSceneQuestForm({
|
|
|
8248
8333
|
{
|
|
8249
8334
|
key: prop.id,
|
|
8250
8335
|
prop,
|
|
8251
|
-
onUpdate: (updatedProp) =>
|
|
8336
|
+
onUpdate: (updatedProp) => handleUpdateProp2(index, updatedProp),
|
|
8252
8337
|
onRemove: () => handleRemoveProp(index),
|
|
8253
8338
|
onOpenImageViewer,
|
|
8254
8339
|
unavailableTypes: currentUsedTypes.filter((t) => t !== prop.type),
|
|
@@ -8314,6 +8399,7 @@ function NodeDetailsPanel({
|
|
|
8314
8399
|
return !!(node == null ? void 0 : node.useImageAsTexture);
|
|
8315
8400
|
});
|
|
8316
8401
|
const [selectedImageUrl, setSelectedImageUrl] = useState17((node == null ? void 0 : node.textureImageUrl) ?? null);
|
|
8402
|
+
const [hasUnsavedChanges, setHasUnsavedChanges2] = useState17(false);
|
|
8317
8403
|
const maxPanelW = typeof window !== "undefined" ? window.innerWidth * 0.92 : 1200;
|
|
8318
8404
|
const { width: panelWidth, isResizing, handlePointerDown: handleResize, setWidth } = useResizablePanel({
|
|
8319
8405
|
initialWidth: isReadMode ? 700 : 440,
|
|
@@ -8351,6 +8437,7 @@ function NodeDetailsPanel({
|
|
|
8351
8437
|
else if ((node == null ? void 0 : node.useImageAsTexture) === "false") setUseImageAsTexture(false);
|
|
8352
8438
|
else setUseImageAsTexture(!!(node == null ? void 0 : node.useImageAsTexture));
|
|
8353
8439
|
setSelectedImageUrl((node == null ? void 0 : node.textureImageUrl) ?? null);
|
|
8440
|
+
setHasUnsavedChanges2(false);
|
|
8354
8441
|
}
|
|
8355
8442
|
}, [node]);
|
|
8356
8443
|
const hasImages = customProps.some((p) => p.type === "images" && Array.isArray(p.value) && p.value.length > 0 && p.value.some((img) => img.value));
|
|
@@ -8377,6 +8464,7 @@ function NodeDetailsPanel({
|
|
|
8377
8464
|
setIntensity(val);
|
|
8378
8465
|
onIntensityChange == null ? void 0 : onIntensityChange(node.id, val);
|
|
8379
8466
|
onDataUpdate == null ? void 0 : onDataUpdate({ ...node, intensity: val });
|
|
8467
|
+
setHasUnsavedChanges2(true);
|
|
8380
8468
|
};
|
|
8381
8469
|
const handleCopyLink = () => {
|
|
8382
8470
|
if (!(node == null ? void 0 : node.id)) return;
|
|
@@ -8394,14 +8482,17 @@ function NodeDetailsPanel({
|
|
|
8394
8482
|
const v = e.target.value;
|
|
8395
8483
|
setName(v);
|
|
8396
8484
|
onNameChange == null ? void 0 : onNameChange(node.id, v);
|
|
8485
|
+
setHasUnsavedChanges2(true);
|
|
8397
8486
|
};
|
|
8398
8487
|
const handleColorChange = (val) => {
|
|
8399
8488
|
setColor(val);
|
|
8400
8489
|
onColorChange == null ? void 0 : onColorChange(node.id, val);
|
|
8490
|
+
setHasUnsavedChanges2(true);
|
|
8401
8491
|
};
|
|
8402
8492
|
const handleSizeChange = (newSize) => {
|
|
8403
8493
|
setSize(newSize);
|
|
8404
8494
|
onSizeChange == null ? void 0 : onSizeChange(node.id, newSize);
|
|
8495
|
+
setHasUnsavedChanges2(true);
|
|
8405
8496
|
};
|
|
8406
8497
|
const handleAddType = (newType) => {
|
|
8407
8498
|
const trimmed = newType.trim();
|
|
@@ -8409,10 +8500,12 @@ function NodeDetailsPanel({
|
|
|
8409
8500
|
setTypes([...types, trimmed]);
|
|
8410
8501
|
setTypeInput("");
|
|
8411
8502
|
setShowTypeSuggestions(false);
|
|
8503
|
+
setHasUnsavedChanges2(true);
|
|
8412
8504
|
}
|
|
8413
8505
|
};
|
|
8414
8506
|
const handleRemoveType = (indexToRemove) => {
|
|
8415
8507
|
setTypes(types.filter((_, index) => index !== indexToRemove));
|
|
8508
|
+
setHasUnsavedChanges2(true);
|
|
8416
8509
|
};
|
|
8417
8510
|
const handleTypeInputKeyDown = (e) => {
|
|
8418
8511
|
if (e.key === "Enter") {
|
|
@@ -8425,6 +8518,7 @@ function NodeDetailsPanel({
|
|
|
8425
8518
|
const handleAddProp = () => {
|
|
8426
8519
|
const newProp = createNewCustomProperty(customProps);
|
|
8427
8520
|
setCustomProps((p) => [...p, newProp]);
|
|
8521
|
+
setHasUnsavedChanges2(true);
|
|
8428
8522
|
setTimeout(() => {
|
|
8429
8523
|
var _a;
|
|
8430
8524
|
(_a = propsEndRef.current) == null ? void 0 : _a.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
@@ -8433,19 +8527,21 @@ function NodeDetailsPanel({
|
|
|
8433
8527
|
const handleRemoveProp = (i) => {
|
|
8434
8528
|
const newProps = customProps.filter((_, idx) => idx !== i);
|
|
8435
8529
|
setCustomProps(newProps);
|
|
8530
|
+
setHasUnsavedChanges2(true);
|
|
8436
8531
|
triggerAutoSave({ customProps: newProps });
|
|
8437
8532
|
};
|
|
8438
|
-
const
|
|
8533
|
+
const handlePropChange = (index, updatedProp) => {
|
|
8439
8534
|
const newProps = [...customProps];
|
|
8440
8535
|
newProps[index] = updatedProp;
|
|
8441
8536
|
setCustomProps(newProps);
|
|
8537
|
+
setHasUnsavedChanges2(true);
|
|
8442
8538
|
if (!updatedProp.isEditing) {
|
|
8443
8539
|
triggerAutoSave({ customProps: newProps });
|
|
8444
8540
|
}
|
|
8445
8541
|
};
|
|
8446
8542
|
const handleToggleImageMode = () => {
|
|
8447
|
-
const newValue = !useImageAsTexture;
|
|
8448
8543
|
setUseImageAsTexture(newValue);
|
|
8544
|
+
setHasUnsavedChanges2(true);
|
|
8449
8545
|
let activeUrl = null;
|
|
8450
8546
|
if (newValue) {
|
|
8451
8547
|
const firstImageProp = customProps.find((p) => p.type === "images");
|
|
@@ -8467,6 +8563,7 @@ function NodeDetailsPanel({
|
|
|
8467
8563
|
};
|
|
8468
8564
|
const handleSelectTexture = (url) => {
|
|
8469
8565
|
setSelectedImageUrl(url);
|
|
8566
|
+
setHasUnsavedChanges2(true);
|
|
8470
8567
|
onImageChange == null ? void 0 : onImageChange(true, url, color);
|
|
8471
8568
|
onDataUpdate == null ? void 0 : onDataUpdate({
|
|
8472
8569
|
...node,
|
|
@@ -8477,6 +8574,7 @@ function NodeDetailsPanel({
|
|
|
8477
8574
|
};
|
|
8478
8575
|
const handleSaveDescriptionInline = (newDescription) => {
|
|
8479
8576
|
setDescription(newDescription);
|
|
8577
|
+
setHasUnsavedChanges2(true);
|
|
8480
8578
|
onDataUpdate({ ...node, description: newDescription });
|
|
8481
8579
|
triggerAutoSave({ description: newDescription });
|
|
8482
8580
|
};
|
|
@@ -8487,6 +8585,10 @@ function NodeDetailsPanel({
|
|
|
8487
8585
|
const currentCustomProps = overrides.customProps !== void 0 ? overrides.customProps : customProps;
|
|
8488
8586
|
const currentExistingSections = overrides.existingSections !== void 0 ? overrides.existingSections : existingSections;
|
|
8489
8587
|
const currentIntensity = overrides.intensity !== void 0 ? overrides.intensity : intensity;
|
|
8588
|
+
if (!keepOpen && !hasUnsavedChanges) {
|
|
8589
|
+
onClose();
|
|
8590
|
+
return;
|
|
8591
|
+
}
|
|
8490
8592
|
if (!currentName.trim() || currentTypes.length === 0) {
|
|
8491
8593
|
alert("O campo 'Nome' e pelo menos um 'Tipo' s\xE3o obrigat\xF3rios.");
|
|
8492
8594
|
return;
|
|
@@ -8511,6 +8613,7 @@ function NodeDetailsPanel({
|
|
|
8511
8613
|
};
|
|
8512
8614
|
await onSave(dataToSave, keepOpen);
|
|
8513
8615
|
onDataUpdate(dataToSave);
|
|
8616
|
+
setHasUnsavedChanges2(false);
|
|
8514
8617
|
if (!keepOpen) {
|
|
8515
8618
|
onClose();
|
|
8516
8619
|
}
|
|
@@ -8806,6 +8909,7 @@ function QuestDetailsPanel({
|
|
|
8806
8909
|
const [existingSections, setExistingSections] = useState18((node == null ? void 0 : node.description_sections) || []);
|
|
8807
8910
|
const [isSaving, setIsSaving] = useState18(false);
|
|
8808
8911
|
const [isLinkCopied, setIsLinkCopied] = useState18(false);
|
|
8912
|
+
const [hasUnsavedChanges, setHasUnsavedChanges2] = useState18(false);
|
|
8809
8913
|
const maxPanelW = typeof window !== "undefined" ? window.innerWidth * 0.92 : 1200;
|
|
8810
8914
|
const { width: panelWidth, isResizing, handlePointerDown: handleResize, setWidth } = useResizablePanel({
|
|
8811
8915
|
initialWidth: isReadMode ? 700 : 440,
|
|
@@ -8837,6 +8941,7 @@ function QuestDetailsPanel({
|
|
|
8837
8941
|
setIntensity((node == null ? void 0 : node.intensity) !== void 0 ? node.intensity : 0);
|
|
8838
8942
|
setExistingSections((node == null ? void 0 : node.description_sections) || []);
|
|
8839
8943
|
setCustomProps(extractCustomPropsFromNode(node || {}));
|
|
8944
|
+
setHasUnsavedChanges2(false);
|
|
8840
8945
|
}
|
|
8841
8946
|
}, [node]);
|
|
8842
8947
|
useEffect16(() => {
|
|
@@ -8868,16 +8973,19 @@ function QuestDetailsPanel({
|
|
|
8868
8973
|
setRawTitle(val);
|
|
8869
8974
|
const newStandardName = questPrefix ? `${questPrefix} - \xBB ${val || "Sem t\xEDtulo"}` : val;
|
|
8870
8975
|
onNameChange == null ? void 0 : onNameChange(node.id, newStandardName, val);
|
|
8976
|
+
setHasUnsavedChanges2(true);
|
|
8871
8977
|
};
|
|
8872
8978
|
const handleSizeChange = (newSize) => {
|
|
8873
8979
|
setSize(newSize);
|
|
8874
8980
|
onSizeChange == null ? void 0 : onSizeChange(node.id, newSize);
|
|
8981
|
+
setHasUnsavedChanges2(true);
|
|
8875
8982
|
};
|
|
8876
8983
|
const handleStatusChange = (newStatus) => {
|
|
8877
8984
|
setStatus(newStatus);
|
|
8878
8985
|
const newColor = QUEST_STATUS_COLORS3[newStatus];
|
|
8879
8986
|
onColorChange == null ? void 0 : onColorChange(node.id, newColor);
|
|
8880
8987
|
onDataUpdate == null ? void 0 : onDataUpdate({ ...node, status: newStatus, color: newColor });
|
|
8988
|
+
setHasUnsavedChanges2(true);
|
|
8881
8989
|
};
|
|
8882
8990
|
const handleAddType = (newType) => {
|
|
8883
8991
|
const trimmed = newType.trim();
|
|
@@ -8885,11 +8993,13 @@ function QuestDetailsPanel({
|
|
|
8885
8993
|
setTypes([...types, trimmed]);
|
|
8886
8994
|
setTypeInput("");
|
|
8887
8995
|
setShowTypeSuggestions(false);
|
|
8996
|
+
setHasUnsavedChanges2(true);
|
|
8888
8997
|
}
|
|
8889
8998
|
};
|
|
8890
8999
|
const handleRemoveType = (indexToRemove) => {
|
|
8891
9000
|
if (types[indexToRemove] === "quest") return;
|
|
8892
9001
|
setTypes(types.filter((_, index) => index !== indexToRemove));
|
|
9002
|
+
setHasUnsavedChanges2(true);
|
|
8893
9003
|
};
|
|
8894
9004
|
const handleTypeInputKeyDown = (e) => {
|
|
8895
9005
|
if (e.key === "Enter") {
|
|
@@ -8902,6 +9012,7 @@ function QuestDetailsPanel({
|
|
|
8902
9012
|
const handleAddProp = () => {
|
|
8903
9013
|
const newProp = createNewCustomProperty(customProps);
|
|
8904
9014
|
setCustomProps((p) => [...p, newProp]);
|
|
9015
|
+
setHasUnsavedChanges2(true);
|
|
8905
9016
|
setTimeout(() => {
|
|
8906
9017
|
var _a2;
|
|
8907
9018
|
(_a2 = propsEndRef.current) == null ? void 0 : _a2.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
@@ -8910,18 +9021,21 @@ function QuestDetailsPanel({
|
|
|
8910
9021
|
const handleRemoveProp = (i) => {
|
|
8911
9022
|
const newProps = customProps.filter((_, idx) => idx !== i);
|
|
8912
9023
|
setCustomProps(newProps);
|
|
9024
|
+
setHasUnsavedChanges2(true);
|
|
8913
9025
|
triggerAutoSave({ customProps: newProps });
|
|
8914
9026
|
};
|
|
8915
|
-
const
|
|
9027
|
+
const handleCustomPropChange = (index, updatedProp) => {
|
|
8916
9028
|
const newProps = [...customProps];
|
|
8917
9029
|
newProps[index] = updatedProp;
|
|
8918
9030
|
setCustomProps(newProps);
|
|
9031
|
+
setHasUnsavedChanges2(true);
|
|
8919
9032
|
if (!updatedProp.isEditing) {
|
|
8920
9033
|
triggerAutoSave({ customProps: newProps });
|
|
8921
9034
|
}
|
|
8922
9035
|
};
|
|
8923
9036
|
const handleSaveDescriptionInline = (newDescription) => {
|
|
8924
9037
|
setDescription(newDescription);
|
|
9038
|
+
setHasUnsavedChanges2(true);
|
|
8925
9039
|
onDataUpdate({ ...node, description: newDescription });
|
|
8926
9040
|
triggerAutoSave({ description: newDescription });
|
|
8927
9041
|
};
|
|
@@ -8933,6 +9047,10 @@ function QuestDetailsPanel({
|
|
|
8933
9047
|
const currentCustomProps = overrides.customProps !== void 0 ? overrides.customProps : customProps;
|
|
8934
9048
|
const currentExistingSections = overrides.existingSections !== void 0 ? overrides.existingSections : existingSections;
|
|
8935
9049
|
const currentStatus = overrides.status !== void 0 ? overrides.status : status;
|
|
9050
|
+
if (!keepOpen && !hasUnsavedChanges) {
|
|
9051
|
+
onClose();
|
|
9052
|
+
return;
|
|
9053
|
+
}
|
|
8936
9054
|
if (!currentRawTitle.trim() || currentTypes.length === 0) {
|
|
8937
9055
|
alert("O campo 'T\xEDtulo' e pelo menos um 'Tipo' s\xE3o obrigat\xF3rios.");
|
|
8938
9056
|
return;
|
|
@@ -8962,6 +9080,7 @@ function QuestDetailsPanel({
|
|
|
8962
9080
|
};
|
|
8963
9081
|
await onSave(dataToSave, keepOpen);
|
|
8964
9082
|
onDataUpdate(dataToSave);
|
|
9083
|
+
setHasUnsavedChanges2(false);
|
|
8965
9084
|
if (!keepOpen) {
|
|
8966
9085
|
onClose();
|
|
8967
9086
|
}
|
|
@@ -9193,9 +9312,12 @@ function RelationshipDetailsPanel({
|
|
|
9193
9312
|
const [description, setDescription] = useState20((link == null ? void 0 : link.description) ?? "");
|
|
9194
9313
|
const [customProps, setCustomProps] = useState20(() => extractCustomPropsFromNode(link || {}));
|
|
9195
9314
|
const [existingSections, setExistingSections] = useState20((link == null ? void 0 : link.description_sections) || []);
|
|
9315
|
+
const [sourceLabel, setSourceLabel] = useState20((link == null ? void 0 : link.source_label) ?? "");
|
|
9316
|
+
const [targetLabel, setTargetLabel] = useState20((link == null ? void 0 : link.target_label) ?? "");
|
|
9196
9317
|
const [isDescriptionModalOpen, setIsDescriptionModalOpen] = useState20(false);
|
|
9197
9318
|
const [isSaving, setIsSaving] = useState20(false);
|
|
9198
9319
|
const [isReadMode, setIsReadMode] = useState20(false);
|
|
9320
|
+
const [hasUnsavedChanges, setHasUnsavedChanges2] = useState20(false);
|
|
9199
9321
|
const propsEndRef = useRef16(null);
|
|
9200
9322
|
const canEdit = useMemo9(() => {
|
|
9201
9323
|
const ability = defineAbilityFor(userRole);
|
|
@@ -9206,12 +9328,16 @@ function RelationshipDetailsPanel({
|
|
|
9206
9328
|
setDescription((link == null ? void 0 : link.description) ?? "");
|
|
9207
9329
|
setExistingSections((link == null ? void 0 : link.description_sections) || []);
|
|
9208
9330
|
setCustomProps(extractCustomPropsFromNode(link || {}));
|
|
9331
|
+
setSourceLabel((link == null ? void 0 : link.source_label) ?? "");
|
|
9332
|
+
setTargetLabel((link == null ? void 0 : link.target_label) ?? "");
|
|
9333
|
+
setHasUnsavedChanges2(false);
|
|
9209
9334
|
}, [link]);
|
|
9210
9335
|
const swallow = (e) => e.stopPropagation();
|
|
9211
9336
|
const handleAddProp = () => {
|
|
9212
9337
|
if (!canEdit) return;
|
|
9213
9338
|
const newProp = createNewCustomProperty(customProps);
|
|
9214
9339
|
setCustomProps((p) => [...p, newProp]);
|
|
9340
|
+
setHasUnsavedChanges2(true);
|
|
9215
9341
|
setTimeout(() => {
|
|
9216
9342
|
var _a;
|
|
9217
9343
|
(_a = propsEndRef.current) == null ? void 0 : _a.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
@@ -9223,6 +9349,12 @@ function RelationshipDetailsPanel({
|
|
|
9223
9349
|
const currentCustomProps = overrides.customProps !== void 0 ? overrides.customProps : customProps;
|
|
9224
9350
|
const currentExistingSections = overrides.existingSections !== void 0 ? overrides.existingSections : existingSections;
|
|
9225
9351
|
const currentName = overrides.name !== void 0 ? overrides.name : name;
|
|
9352
|
+
const currentSourceLabel = overrides.sourceLabel !== void 0 ? overrides.sourceLabel : sourceLabel;
|
|
9353
|
+
const currentTargetLabel = overrides.targetLabel !== void 0 ? overrides.targetLabel : targetLabel;
|
|
9354
|
+
if (!keepOpen && !hasUnsavedChanges) {
|
|
9355
|
+
onClose();
|
|
9356
|
+
return;
|
|
9357
|
+
}
|
|
9226
9358
|
setIsSaving(true);
|
|
9227
9359
|
try {
|
|
9228
9360
|
const extrasObj = toObjectFromCustomProps(currentCustomProps.filter((p) => !p.isEditing));
|
|
@@ -9238,8 +9370,11 @@ function RelationshipDetailsPanel({
|
|
|
9238
9370
|
isCurved: link.isCurved,
|
|
9239
9371
|
curveOffset: link.curveOffset
|
|
9240
9372
|
};
|
|
9373
|
+
if (currentSourceLabel.trim()) dataToSave.source_label = currentSourceLabel.trim();
|
|
9374
|
+
if (currentTargetLabel.trim()) dataToSave.target_label = currentTargetLabel.trim();
|
|
9241
9375
|
await onSave(dataToSave, keepOpen);
|
|
9242
9376
|
onDataUpdate(dataToSave);
|
|
9377
|
+
setHasUnsavedChanges2(false);
|
|
9243
9378
|
if (!keepOpen) {
|
|
9244
9379
|
onClose();
|
|
9245
9380
|
}
|
|
@@ -9253,18 +9388,21 @@ function RelationshipDetailsPanel({
|
|
|
9253
9388
|
const handleSaveDescriptionInline = (newDescription) => {
|
|
9254
9389
|
if (!canEdit) return;
|
|
9255
9390
|
setDescription(newDescription);
|
|
9391
|
+
setHasUnsavedChanges2(true);
|
|
9256
9392
|
onDataUpdate((prev) => ({ ...prev, description: newDescription }));
|
|
9257
9393
|
triggerAutoSave({ description: newDescription });
|
|
9258
9394
|
};
|
|
9259
9395
|
const handleRemoveProp = (i) => {
|
|
9260
9396
|
const newProps = customProps.filter((_, idx) => idx !== i);
|
|
9261
9397
|
setCustomProps(newProps);
|
|
9398
|
+
setHasUnsavedChanges2(true);
|
|
9262
9399
|
triggerAutoSave({ customProps: newProps });
|
|
9263
9400
|
};
|
|
9264
|
-
const
|
|
9401
|
+
const handleUpdateProp2 = (index, updatedProp) => {
|
|
9265
9402
|
const newProps = [...customProps];
|
|
9266
9403
|
newProps[index] = updatedProp;
|
|
9267
9404
|
setCustomProps(newProps);
|
|
9405
|
+
setHasUnsavedChanges2(true);
|
|
9268
9406
|
if (!updatedProp.isEditing) {
|
|
9269
9407
|
triggerAutoSave({ customProps: newProps });
|
|
9270
9408
|
}
|
|
@@ -9317,14 +9455,47 @@ function RelationshipDetailsPanel({
|
|
|
9317
9455
|
{
|
|
9318
9456
|
type: "text",
|
|
9319
9457
|
value: name,
|
|
9320
|
-
onChange: (e) =>
|
|
9458
|
+
onChange: (e) => {
|
|
9459
|
+
setName(e.target.value);
|
|
9460
|
+
setHasUnsavedChanges2(true);
|
|
9461
|
+
},
|
|
9321
9462
|
placeholder: "Ex: Controla, Pertence a, Fornece...",
|
|
9322
9463
|
disabled: !canEdit,
|
|
9323
9464
|
className: `w-full bg-slate-800/70 p-2 text-sm rounded-lg border border-white/10 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-indigo-400/60
|
|
9324
9465
|
${!canEdit ? "opacity-50 cursor-not-allowed" : ""}
|
|
9325
9466
|
`
|
|
9326
9467
|
}
|
|
9327
|
-
)), /* @__PURE__ */ React19.createElement("div", { className: "space-y-1.
|
|
9468
|
+
)), /* @__PURE__ */ React19.createElement("div", { className: "rounded-xl border border-white/8 bg-slate-800/30 p-3 space-y-3" }, /* @__PURE__ */ React19.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React19.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "13", height: "13", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "text-violet-400 flex-shrink-0" }, /* @__PURE__ */ React19.createElement("path", { d: "M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z" }), /* @__PURE__ */ React19.createElement("line", { x1: "7", y1: "7", x2: "7.01", y2: "7" })), /* @__PURE__ */ React19.createElement("p", { className: "text-xs font-medium text-violet-300/80 uppercase tracking-wider" }, "Labels de Agrupamento")), /* @__PURE__ */ React19.createElement("p", { className: "text-[11px] text-slate-400 leading-relaxed" }, "Labels agrupam conex\xF5es no menu de Conex\xF5es. Cada lado da conex\xE3o pode ter sua pr\xF3pria label."), /* @__PURE__ */ React19.createElement("div", { className: "grid grid-cols-2 gap-2" }, /* @__PURE__ */ React19.createElement("div", { className: "space-y-1" }, /* @__PURE__ */ React19.createElement("label", { className: "text-[11px] text-slate-400" }, "Label do Source"), /* @__PURE__ */ React19.createElement(
|
|
9469
|
+
"input",
|
|
9470
|
+
{
|
|
9471
|
+
type: "text",
|
|
9472
|
+
value: sourceLabel,
|
|
9473
|
+
onChange: (e) => {
|
|
9474
|
+
setSourceLabel(e.target.value);
|
|
9475
|
+
setHasUnsavedChanges2(true);
|
|
9476
|
+
},
|
|
9477
|
+
placeholder: "Ex: Conceitos",
|
|
9478
|
+
disabled: !canEdit,
|
|
9479
|
+
className: `w-full bg-slate-900/60 p-2 text-xs rounded-lg border border-white/10 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-violet-400/60 placeholder:text-slate-600
|
|
9480
|
+
${!canEdit ? "opacity-50 cursor-not-allowed" : ""}
|
|
9481
|
+
`
|
|
9482
|
+
}
|
|
9483
|
+
)), /* @__PURE__ */ React19.createElement("div", { className: "space-y-1" }, /* @__PURE__ */ React19.createElement("label", { className: "text-[11px] text-slate-400" }, "Label do Target"), /* @__PURE__ */ React19.createElement(
|
|
9484
|
+
"input",
|
|
9485
|
+
{
|
|
9486
|
+
type: "text",
|
|
9487
|
+
value: targetLabel,
|
|
9488
|
+
onChange: (e) => {
|
|
9489
|
+
setTargetLabel(e.target.value);
|
|
9490
|
+
setHasUnsavedChanges2(true);
|
|
9491
|
+
},
|
|
9492
|
+
placeholder: "Ex: Refer\xEAncias",
|
|
9493
|
+
disabled: !canEdit,
|
|
9494
|
+
className: `w-full bg-slate-900/60 p-2 text-xs rounded-lg border border-white/10 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-violet-400/60 placeholder:text-slate-600
|
|
9495
|
+
${!canEdit ? "opacity-50 cursor-not-allowed" : ""}
|
|
9496
|
+
`
|
|
9497
|
+
}
|
|
9498
|
+
)))), /* @__PURE__ */ React19.createElement("div", { className: "space-y-1.5 relative" }, /* @__PURE__ */ React19.createElement("label", { className: "text-xs text-slate-300" }, "Descri\xE7\xE3o"), /* @__PURE__ */ React19.createElement("div", { className: "relative group min-h-[60px] bg-slate-800/40 rounded-lg border border-white/10 hover:border-white/20 transition-colors" }, /* @__PURE__ */ React19.createElement(
|
|
9328
9499
|
DescriptionDisplay,
|
|
9329
9500
|
{
|
|
9330
9501
|
description,
|
|
@@ -9366,7 +9537,7 @@ function RelationshipDetailsPanel({
|
|
|
9366
9537
|
{
|
|
9367
9538
|
key: prop.id,
|
|
9368
9539
|
prop,
|
|
9369
|
-
onUpdate: (updatedProp) =>
|
|
9540
|
+
onUpdate: (updatedProp) => handleUpdateProp2(idx, updatedProp),
|
|
9370
9541
|
onRemove: () => handleRemoveProp(idx),
|
|
9371
9542
|
onOpenImageViewer,
|
|
9372
9543
|
unavailableTypes: currentUsedTypes.filter((t) => t !== prop.type),
|