@lv-x-software-house/x_view 1.2.6-dev.12 → 1.2.6-dev.14
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 +193 -35
- package/dist/index.mjs +196 -36
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -13079,6 +13079,7 @@ function RichGroupEditor({
|
|
|
13079
13079
|
}) {
|
|
13080
13080
|
const editorRef = (0, import_react28.useRef)(null);
|
|
13081
13081
|
const isMountedRef = (0, import_react28.useRef)(false);
|
|
13082
|
+
const dismissedTriggerRef = (0, import_react28.useRef)(null);
|
|
13082
13083
|
const [mentionState, setMentionState] = (0, import_react28.useState)(null);
|
|
13083
13084
|
const [isEmpty, setIsEmpty] = (0, import_react28.useState)(true);
|
|
13084
13085
|
(0, import_react28.useEffect)(() => {
|
|
@@ -13094,10 +13095,17 @@ function RichGroupEditor({
|
|
|
13094
13095
|
const selection = window.getSelection();
|
|
13095
13096
|
if (!selection || selection.rangeCount === 0) return null;
|
|
13096
13097
|
const range = selection.getRangeAt(0);
|
|
13097
|
-
|
|
13098
|
-
|
|
13098
|
+
const node = range.startContainer;
|
|
13099
|
+
if (node.nodeType !== Node.TEXT_NODE) return null;
|
|
13100
|
+
const textBefore = node.textContent.substring(0, range.startOffset);
|
|
13099
13101
|
const atIndex = textBefore.lastIndexOf("@");
|
|
13100
|
-
if (atIndex === -1)
|
|
13102
|
+
if (atIndex === -1) {
|
|
13103
|
+
dismissedTriggerRef.current = null;
|
|
13104
|
+
return null;
|
|
13105
|
+
}
|
|
13106
|
+
if (dismissedTriggerRef.current && dismissedTriggerRef.current.node === node && dismissedTriggerRef.current.atIndex === atIndex) {
|
|
13107
|
+
return null;
|
|
13108
|
+
}
|
|
13101
13109
|
const query = textBefore.substring(atIndex + 1);
|
|
13102
13110
|
if (query.includes(" ") || query.includes(".") || query.includes(",") || query.length > 40) {
|
|
13103
13111
|
return null;
|
|
@@ -13232,6 +13240,21 @@ function RichGroupEditor({
|
|
|
13232
13240
|
const handleBlur = (0, import_react28.useCallback)(() => {
|
|
13233
13241
|
setTimeout(() => setMentionState(null), 200);
|
|
13234
13242
|
}, []);
|
|
13243
|
+
const handleMentionDismiss = (0, import_react28.useCallback)(() => {
|
|
13244
|
+
const selection = window.getSelection();
|
|
13245
|
+
if (selection && selection.rangeCount > 0) {
|
|
13246
|
+
const range = selection.getRangeAt(0);
|
|
13247
|
+
const node = range.startContainer;
|
|
13248
|
+
if (node.nodeType === Node.TEXT_NODE) {
|
|
13249
|
+
const textBefore = node.textContent.substring(0, range.startOffset);
|
|
13250
|
+
const atIndex = textBefore.lastIndexOf("@");
|
|
13251
|
+
if (atIndex !== -1) {
|
|
13252
|
+
dismissedTriggerRef.current = { node, atIndex };
|
|
13253
|
+
}
|
|
13254
|
+
}
|
|
13255
|
+
}
|
|
13256
|
+
setMentionState(null);
|
|
13257
|
+
}, []);
|
|
13235
13258
|
return /* @__PURE__ */ import_react28.default.createElement("div", { className: "relative" }, isEmpty && !readOnly && /* @__PURE__ */ import_react28.default.createElement("div", { className: "absolute top-0 left-0 text-sm text-slate-600 pointer-events-none select-none" }, placeholder || "Escreva sobre este grupo\u2026 ou digite @ para mencionar"), /* @__PURE__ */ import_react28.default.createElement(
|
|
13236
13259
|
"div",
|
|
13237
13260
|
{
|
|
@@ -13253,14 +13276,14 @@ function RichGroupEditor({
|
|
|
13253
13276
|
items: searchItems || [],
|
|
13254
13277
|
position: mentionState.position,
|
|
13255
13278
|
onSelect: insertMention,
|
|
13256
|
-
onClose:
|
|
13279
|
+
onClose: handleMentionDismiss
|
|
13257
13280
|
}
|
|
13258
13281
|
));
|
|
13259
13282
|
}
|
|
13260
13283
|
|
|
13261
13284
|
// src/components/AncestryBoard.jsx
|
|
13262
|
-
var
|
|
13263
|
-
|
|
13285
|
+
var BoardItem = ({
|
|
13286
|
+
item,
|
|
13264
13287
|
index,
|
|
13265
13288
|
onUpdate,
|
|
13266
13289
|
onDelete,
|
|
@@ -13269,16 +13292,55 @@ var GroupItem = ({
|
|
|
13269
13292
|
onOutdent,
|
|
13270
13293
|
searchItems,
|
|
13271
13294
|
onMentionClick,
|
|
13295
|
+
onEnterFolder,
|
|
13272
13296
|
canEdit
|
|
13273
13297
|
}) => {
|
|
13298
|
+
var _a;
|
|
13299
|
+
const isFolder = item.type === "folder";
|
|
13274
13300
|
const canIndent = index > 0;
|
|
13275
13301
|
const handleContentChange = (content) => {
|
|
13276
|
-
onUpdate(
|
|
13302
|
+
onUpdate(item.id, { ...item, content });
|
|
13303
|
+
};
|
|
13304
|
+
const handleNameChange = (e) => {
|
|
13305
|
+
onUpdate(item.id, { ...item, name: e.target.value });
|
|
13277
13306
|
};
|
|
13307
|
+
if (isFolder) {
|
|
13308
|
+
return /* @__PURE__ */ import_react29.default.createElement("div", { className: "flex flex-col gap-2 mb-3 relative group/item animate-in fade-in slide-in-from-left-2 duration-300" }, /* @__PURE__ */ import_react29.default.createElement(
|
|
13309
|
+
"div",
|
|
13310
|
+
{
|
|
13311
|
+
onClick: () => onEnterFolder(item.id),
|
|
13312
|
+
className: "flex items-center gap-3 py-3 px-4 bg-white/5 hover:bg-white/10 border border-white/10 hover:border-white/20 rounded-xl cursor-pointer transition-all duration-200 group/folder"
|
|
13313
|
+
},
|
|
13314
|
+
/* @__PURE__ */ import_react29.default.createElement("div", { className: "w-10 h-10 rounded-lg bg-indigo-500/10 flex items-center justify-center text-indigo-400 group-hover/folder:scale-110 transition-transform" }, /* @__PURE__ */ import_react29.default.createElement(import_fi23.FiFolder, { size: 20 })),
|
|
13315
|
+
/* @__PURE__ */ import_react29.default.createElement("div", { className: "flex-1 min-w-0" }, canEdit ? /* @__PURE__ */ import_react29.default.createElement(
|
|
13316
|
+
"input",
|
|
13317
|
+
{
|
|
13318
|
+
type: "text",
|
|
13319
|
+
value: item.name,
|
|
13320
|
+
onChange: handleNameChange,
|
|
13321
|
+
onClick: (e) => e.stopPropagation(),
|
|
13322
|
+
className: "bg-transparent border-none outline-none text-sm font-medium text-slate-200 w-full focus:text-white",
|
|
13323
|
+
placeholder: "Nome da pasta..."
|
|
13324
|
+
}
|
|
13325
|
+
) : /* @__PURE__ */ import_react29.default.createElement("span", { className: "text-sm font-medium text-slate-200" }, item.name), /* @__PURE__ */ import_react29.default.createElement("div", { className: "flex items-center gap-2 mt-0.5" }, /* @__PURE__ */ import_react29.default.createElement("span", { className: "text-[10px] text-slate-500 uppercase tracking-wider font-semibold" }, ((_a = item.children) == null ? void 0 : _a.length) || 0, " itens"))),
|
|
13326
|
+
/* @__PURE__ */ import_react29.default.createElement("div", { className: "flex items-center gap-2 opacity-0 group-hover/folder:opacity-100 transition-opacity" }, canEdit && /* @__PURE__ */ import_react29.default.createElement(
|
|
13327
|
+
"button",
|
|
13328
|
+
{
|
|
13329
|
+
onClick: (e) => {
|
|
13330
|
+
e.stopPropagation();
|
|
13331
|
+
onDelete(item.id);
|
|
13332
|
+
},
|
|
13333
|
+
className: "p-1.5 text-slate-500 hover:text-red-400 hover:bg-red-500/10 rounded transition-colors",
|
|
13334
|
+
title: "Remover Pasta"
|
|
13335
|
+
},
|
|
13336
|
+
/* @__PURE__ */ import_react29.default.createElement(import_fi23.FiTrash2, { size: 14 })
|
|
13337
|
+
), /* @__PURE__ */ import_react29.default.createElement(import_fi23.FiChevronRight, { className: "text-slate-500 group-hover/folder:translate-x-1 transition-transform" }))
|
|
13338
|
+
));
|
|
13339
|
+
}
|
|
13278
13340
|
return /* @__PURE__ */ import_react29.default.createElement("div", { className: "flex flex-col gap-2 mb-3 pl-3 border-l border-white/10 relative group/item animate-in fade-in slide-in-from-left-2 duration-300" }, /* @__PURE__ */ import_react29.default.createElement("div", { className: "absolute -left-[1px] top-4 w-2 h-px bg-white/20" }), /* @__PURE__ */ import_react29.default.createElement("div", { className: "flex flex-col gap-2 py-2 px-3 transition-all duration-200 hover:bg-white/5 border-l-2 border-transparent hover:border-white/20" }, /* @__PURE__ */ import_react29.default.createElement(
|
|
13279
13341
|
RichGroupEditor,
|
|
13280
13342
|
{
|
|
13281
|
-
content:
|
|
13343
|
+
content: item.content,
|
|
13282
13344
|
onChange: handleContentChange,
|
|
13283
13345
|
onMentionClick,
|
|
13284
13346
|
searchItems,
|
|
@@ -13288,15 +13350,25 @@ var GroupItem = ({
|
|
|
13288
13350
|
), canEdit && /* @__PURE__ */ import_react29.default.createElement("div", { className: "flex items-center justify-between pt-2 mt-1 border-t border-white/5 opacity-40 group-hover/item:opacity-100 transition-opacity" }, /* @__PURE__ */ import_react29.default.createElement("div", { className: "flex items-center gap-1" }, /* @__PURE__ */ import_react29.default.createElement(
|
|
13289
13351
|
"button",
|
|
13290
13352
|
{
|
|
13291
|
-
onClick: () => onAddSubgroup(
|
|
13292
|
-
className: "
|
|
13353
|
+
onClick: () => onAddSubgroup(item.id, "group"),
|
|
13354
|
+
className: "flex items-center gap-1 px-1.5 py-1 text-[10px] uppercase font-bold tracking-wider text-slate-500 hover:text-white hover:bg-white/10 rounded transition-colors",
|
|
13293
13355
|
title: "Criar Subgrupo"
|
|
13294
13356
|
},
|
|
13295
|
-
/* @__PURE__ */ import_react29.default.createElement(import_fi23.FiPlus, { size:
|
|
13357
|
+
/* @__PURE__ */ import_react29.default.createElement(import_fi23.FiPlus, { size: 12 }),
|
|
13358
|
+
/* @__PURE__ */ import_react29.default.createElement("span", null, "Subgrupo")
|
|
13359
|
+
), /* @__PURE__ */ import_react29.default.createElement(
|
|
13360
|
+
"button",
|
|
13361
|
+
{
|
|
13362
|
+
onClick: () => onAddSubgroup(item.id, "folder"),
|
|
13363
|
+
className: "flex items-center gap-1 px-1.5 py-1 text-[10px] uppercase font-bold tracking-wider text-slate-500 hover:text-white hover:bg-white/10 rounded transition-colors",
|
|
13364
|
+
title: "Criar Subpasta"
|
|
13365
|
+
},
|
|
13366
|
+
/* @__PURE__ */ import_react29.default.createElement(import_fi23.FiFolder, { size: 12 }),
|
|
13367
|
+
/* @__PURE__ */ import_react29.default.createElement("span", null, "Pasta")
|
|
13296
13368
|
)), /* @__PURE__ */ import_react29.default.createElement("div", { className: "flex items-center gap-1" }, /* @__PURE__ */ import_react29.default.createElement(
|
|
13297
13369
|
"button",
|
|
13298
13370
|
{
|
|
13299
|
-
onClick: () => onIndent(
|
|
13371
|
+
onClick: () => onIndent(item.id),
|
|
13300
13372
|
disabled: !canIndent,
|
|
13301
13373
|
className: `p-1.5 rounded transition-colors ${!canIndent ? "text-slate-800 cursor-not-allowed" : "text-slate-500 hover:text-white hover:bg-white/10"}`,
|
|
13302
13374
|
title: "Aninhar no grupo acima"
|
|
@@ -13305,7 +13377,7 @@ var GroupItem = ({
|
|
|
13305
13377
|
), /* @__PURE__ */ import_react29.default.createElement(
|
|
13306
13378
|
"button",
|
|
13307
13379
|
{
|
|
13308
|
-
onClick: () => onOutdent(
|
|
13380
|
+
onClick: () => onOutdent(item.id),
|
|
13309
13381
|
className: "p-1.5 text-slate-500 hover:text-white hover:bg-white/10 rounded transition-colors",
|
|
13310
13382
|
title: "Desaninhar"
|
|
13311
13383
|
},
|
|
@@ -13313,17 +13385,17 @@ var GroupItem = ({
|
|
|
13313
13385
|
), /* @__PURE__ */ import_react29.default.createElement("div", { className: "w-px h-3 bg-white/10 mx-1" }), /* @__PURE__ */ import_react29.default.createElement(
|
|
13314
13386
|
"button",
|
|
13315
13387
|
{
|
|
13316
|
-
onClick: () => onDelete(
|
|
13388
|
+
onClick: () => onDelete(item.id),
|
|
13317
13389
|
className: "p-1.5 text-slate-600 hover:text-red-400 hover:bg-red-500/10 rounded transition-colors",
|
|
13318
13390
|
title: "Remover Grupo"
|
|
13319
13391
|
},
|
|
13320
13392
|
/* @__PURE__ */ import_react29.default.createElement(import_fi23.FiTrash2, { size: 14 })
|
|
13321
|
-
)))),
|
|
13322
|
-
|
|
13393
|
+
)))), item.children && item.children.length > 0 && /* @__PURE__ */ import_react29.default.createElement("div", { className: "ml-2" }, item.children.map((child, idx) => /* @__PURE__ */ import_react29.default.createElement(
|
|
13394
|
+
BoardItem,
|
|
13323
13395
|
{
|
|
13324
13396
|
key: child.id,
|
|
13325
13397
|
index: idx,
|
|
13326
|
-
|
|
13398
|
+
item: child,
|
|
13327
13399
|
onUpdate,
|
|
13328
13400
|
onDelete,
|
|
13329
13401
|
onAddSubgroup,
|
|
@@ -13331,6 +13403,7 @@ var GroupItem = ({
|
|
|
13331
13403
|
onOutdent,
|
|
13332
13404
|
searchItems,
|
|
13333
13405
|
onMentionClick,
|
|
13406
|
+
onEnterFolder,
|
|
13334
13407
|
canEdit
|
|
13335
13408
|
}
|
|
13336
13409
|
))));
|
|
@@ -13348,13 +13421,15 @@ function AncestryBoard({
|
|
|
13348
13421
|
const [groups, setGroups] = (0, import_react29.useState)([]);
|
|
13349
13422
|
const [isLoaded, setIsLoaded] = (0, import_react29.useState)(false);
|
|
13350
13423
|
const [saveStatus, setSaveStatus] = (0, import_react29.useState)("idle");
|
|
13424
|
+
const [navigationStack, setNavigationStack] = (0, import_react29.useState)([]);
|
|
13351
13425
|
const canEdit = (0, import_react29.useMemo)(() => userRole !== "viewer", [userRole]);
|
|
13352
13426
|
(0, import_react29.useEffect)(() => {
|
|
13353
13427
|
if (initialGroups && !isLoaded) {
|
|
13354
13428
|
const migrate = (list) => list.map((g) => ({
|
|
13355
13429
|
id: g.id || (0, import_uuid11.v4)(),
|
|
13430
|
+
type: g.type || "group",
|
|
13431
|
+
name: g.name || "",
|
|
13356
13432
|
content: Array.isArray(g.content) ? g.content : [],
|
|
13357
|
-
// descarta dados legados
|
|
13358
13433
|
children: migrate(g.children || [])
|
|
13359
13434
|
}));
|
|
13360
13435
|
setGroups(migrate(initialGroups));
|
|
@@ -13389,6 +13464,8 @@ function AncestryBoard({
|
|
|
13389
13464
|
}, [availableAncestries, availableNodes]);
|
|
13390
13465
|
const sanitizeGroups = (list) => list.map((g) => ({
|
|
13391
13466
|
id: g.id,
|
|
13467
|
+
type: g.type || "group",
|
|
13468
|
+
name: g.name || "",
|
|
13392
13469
|
content: g.content || [],
|
|
13393
13470
|
children: sanitizeGroups(g.children || [])
|
|
13394
13471
|
}));
|
|
@@ -13419,23 +13496,72 @@ function AncestryBoard({
|
|
|
13419
13496
|
window.addEventListener("keydown", handler);
|
|
13420
13497
|
return () => window.removeEventListener("keydown", handler);
|
|
13421
13498
|
}, [isOpen, onClose]);
|
|
13422
|
-
const
|
|
13423
|
-
|
|
13499
|
+
const currentPath = (0, import_react29.useMemo)(() => {
|
|
13500
|
+
const path = [];
|
|
13501
|
+
let currentLevel = groups;
|
|
13502
|
+
for (const folderId of navigationStack) {
|
|
13503
|
+
const folder = currentLevel.find((f) => f.id === folderId);
|
|
13504
|
+
if (folder) {
|
|
13505
|
+
path.push(folder);
|
|
13506
|
+
currentLevel = folder.children;
|
|
13507
|
+
}
|
|
13508
|
+
}
|
|
13509
|
+
return path;
|
|
13510
|
+
}, [groups, navigationStack]);
|
|
13511
|
+
const currentGroups = (0, import_react29.useMemo)(() => {
|
|
13512
|
+
const lastFolder = currentPath[currentPath.length - 1];
|
|
13513
|
+
return lastFolder ? lastFolder.children : groups;
|
|
13514
|
+
}, [groups, currentPath]);
|
|
13515
|
+
const navigateUp = () => {
|
|
13516
|
+
setNavigationStack((prev) => prev.slice(0, -1));
|
|
13517
|
+
};
|
|
13518
|
+
const navigateTo = (folderId) => {
|
|
13519
|
+
setNavigationStack((prev) => [...prev, folderId]);
|
|
13424
13520
|
};
|
|
13425
|
-
const
|
|
13426
|
-
|
|
13521
|
+
const navigateToBreadcrumb = (index) => {
|
|
13522
|
+
setNavigationStack((prev) => prev.slice(0, index + 1));
|
|
13523
|
+
};
|
|
13524
|
+
const navigateHome = () => {
|
|
13525
|
+
setNavigationStack([]);
|
|
13526
|
+
};
|
|
13527
|
+
const handleAddItem = (type = "group") => {
|
|
13528
|
+
const newItem = {
|
|
13529
|
+
id: (0, import_uuid11.v4)(),
|
|
13530
|
+
type,
|
|
13531
|
+
name: type === "folder" ? "Nova Pasta" : "",
|
|
13532
|
+
content: [],
|
|
13533
|
+
children: []
|
|
13534
|
+
};
|
|
13535
|
+
if (navigationStack.length === 0) {
|
|
13536
|
+
setGroups((prev) => [newItem, ...prev]);
|
|
13537
|
+
} else {
|
|
13538
|
+
const parentId = navigationStack[navigationStack.length - 1];
|
|
13539
|
+
const add = (list) => list.map(
|
|
13540
|
+
(g) => g.id === parentId ? { ...g, children: [newItem, ...g.children] } : { ...g, children: add(g.children) }
|
|
13541
|
+
);
|
|
13542
|
+
setGroups((prev) => add(prev));
|
|
13543
|
+
}
|
|
13544
|
+
};
|
|
13545
|
+
const handleAddSubgroup = (parentId, type = "group") => {
|
|
13546
|
+
const newItem = {
|
|
13547
|
+
id: (0, import_uuid11.v4)(),
|
|
13548
|
+
type,
|
|
13549
|
+
name: type === "folder" ? "Nova Pasta" : "",
|
|
13550
|
+
content: [],
|
|
13551
|
+
children: []
|
|
13552
|
+
};
|
|
13427
13553
|
const add = (list) => list.map(
|
|
13428
|
-
(g) => g.id === parentId ? { ...g, children: [
|
|
13554
|
+
(g) => g.id === parentId ? { ...g, children: [newItem, ...g.children] } : { ...g, children: add(g.children) }
|
|
13429
13555
|
);
|
|
13430
13556
|
setGroups((prev) => add(prev));
|
|
13431
13557
|
};
|
|
13432
|
-
const
|
|
13558
|
+
const handleUpdateItem = (id, newData) => {
|
|
13433
13559
|
const update = (list) => list.map(
|
|
13434
13560
|
(g) => g.id === id ? newData : { ...g, children: update(g.children) }
|
|
13435
13561
|
);
|
|
13436
13562
|
setGroups((prev) => update(prev));
|
|
13437
13563
|
};
|
|
13438
|
-
const
|
|
13564
|
+
const handleDeleteItem = (id) => {
|
|
13439
13565
|
const del = (list) => list.filter((g) => g.id !== id).map((g) => ({ ...g, children: del(g.children) }));
|
|
13440
13566
|
setGroups((prev) => del(prev));
|
|
13441
13567
|
};
|
|
@@ -13493,15 +13619,23 @@ function AncestryBoard({
|
|
|
13493
13619
|
className: "bg-slate-950 border border-white/10 rounded-xl w-[98vw] h-[97vh] flex flex-col shadow-2xl overflow-hidden animate-in fade-in zoom-in-95 duration-200",
|
|
13494
13620
|
onClick: (e) => e.stopPropagation()
|
|
13495
13621
|
},
|
|
13496
|
-
/* @__PURE__ */ import_react29.default.createElement("div", { className: "h-14 px-4 border-b border-white/10 bg-slate-900/90 flex items-center justify-between shrink-0" }, /* @__PURE__ */ import_react29.default.createElement("div", { className: "flex items-center gap-4" }, /* @__PURE__ */ import_react29.default.createElement("h3", { className: "text-base font-semibold text-white flex items-center gap-2 whitespace-nowrap" }, /* @__PURE__ */ import_react29.default.createElement(import_fi23.FiLayers, { className: "text-indigo-400" }), "Ancestry Board"), saveStatus !== "idle" && /* @__PURE__ */ import_react29.default.createElement("div", { className: "flex items-center gap-2 animate-in fade-in slide-in-from-left-2 duration-300" }, /* @__PURE__ */ import_react29.default.createElement("div", { className: "w-px h-4 bg-white/10 mx-1" }), /* @__PURE__ */ import_react29.default.createElement("div", { className: "flex items-center gap-1.5 px-2 py-0.5 rounded-full bg-slate-900/50 border border-white/5" }, saveStatus === "saving" && /* @__PURE__ */ import_react29.default.createElement(import_react29.default.Fragment, null, /* @__PURE__ */ import_react29.default.createElement(import_fi23.FiLoader, { className: "animate-spin text-indigo-400", size: 12 }), /* @__PURE__ */ import_react29.default.createElement("span", { className: "text-[10px] uppercase tracking-wide font-medium text-indigo-300" }, "Salvando")), saveStatus === "saved" && /* @__PURE__ */ import_react29.default.createElement(import_react29.default.Fragment, null, /* @__PURE__ */ import_react29.default.createElement(import_fi23.FiCheckCircle, { className: "text-emerald-400", size: 12 }), /* @__PURE__ */ import_react29.default.createElement("span", { className: "text-[10px] uppercase tracking-wide font-medium text-slate-400" }, "Salvo")), saveStatus === "error" && /* @__PURE__ */ import_react29.default.createElement(import_react29.default.Fragment, null, /* @__PURE__ */ import_react29.default.createElement("span", { className: "w-2 h-2 rounded-full bg-red-500" }), /* @__PURE__ */ import_react29.default.createElement("span", { className: "text-[10px] uppercase tracking-wide font-medium text-red-400" }, "Erro"))))), /* @__PURE__ */ import_react29.default.createElement("div", { className: "flex items-center gap-3" }, canEdit && /* @__PURE__ */ import_react29.default.createElement(
|
|
13622
|
+
/* @__PURE__ */ import_react29.default.createElement("div", { className: "h-14 px-4 border-b border-white/10 bg-slate-900/90 flex items-center justify-between shrink-0" }, /* @__PURE__ */ import_react29.default.createElement("div", { className: "flex items-center gap-4" }, /* @__PURE__ */ import_react29.default.createElement("h3", { className: "text-base font-semibold text-white flex items-center gap-2 whitespace-nowrap" }, /* @__PURE__ */ import_react29.default.createElement(import_fi23.FiLayers, { className: "text-indigo-400" }), "Ancestry Board"), saveStatus !== "idle" && /* @__PURE__ */ import_react29.default.createElement("div", { className: "flex items-center gap-2 animate-in fade-in slide-in-from-left-2 duration-300" }, /* @__PURE__ */ import_react29.default.createElement("div", { className: "w-px h-4 bg-white/10 mx-1" }), /* @__PURE__ */ import_react29.default.createElement("div", { className: "flex items-center gap-1.5 px-2 py-0.5 rounded-full bg-slate-900/50 border border-white/5" }, saveStatus === "saving" && /* @__PURE__ */ import_react29.default.createElement(import_react29.default.Fragment, null, /* @__PURE__ */ import_react29.default.createElement(import_fi23.FiLoader, { className: "animate-spin text-indigo-400", size: 12 }), /* @__PURE__ */ import_react29.default.createElement("span", { className: "text-[10px] uppercase tracking-wide font-medium text-indigo-300" }, "Salvando")), saveStatus === "saved" && /* @__PURE__ */ import_react29.default.createElement(import_react29.default.Fragment, null, /* @__PURE__ */ import_react29.default.createElement(import_fi23.FiCheckCircle, { className: "text-emerald-400", size: 12 }), /* @__PURE__ */ import_react29.default.createElement("span", { className: "text-[10px] uppercase tracking-wide font-medium text-slate-400" }, "Salvo")), saveStatus === "error" && /* @__PURE__ */ import_react29.default.createElement(import_react29.default.Fragment, null, /* @__PURE__ */ import_react29.default.createElement("span", { className: "w-2 h-2 rounded-full bg-red-500" }), /* @__PURE__ */ import_react29.default.createElement("span", { className: "text-[10px] uppercase tracking-wide font-medium text-red-400" }, "Erro"))))), /* @__PURE__ */ import_react29.default.createElement("div", { className: "flex items-center gap-3" }, canEdit && /* @__PURE__ */ import_react29.default.createElement(import_react29.default.Fragment, null, /* @__PURE__ */ import_react29.default.createElement(
|
|
13497
13623
|
"button",
|
|
13498
13624
|
{
|
|
13499
|
-
onClick:
|
|
13625
|
+
onClick: () => handleAddItem("folder"),
|
|
13626
|
+
className: "flex items-center gap-2 px-3 py-1.5 bg-white/5 hover:bg-white/10 border border-white/10 hover:border-white/20 backdrop-blur-sm text-slate-200 hover:text-white rounded-md transition-all duration-200 text-xs font-medium shadow-sm"
|
|
13627
|
+
},
|
|
13628
|
+
/* @__PURE__ */ import_react29.default.createElement(import_fi23.FiPlus, { size: 14, className: "text-amber-400" }),
|
|
13629
|
+
/* @__PURE__ */ import_react29.default.createElement("span", { className: "hidden sm:inline" }, "Nova Pasta")
|
|
13630
|
+
), /* @__PURE__ */ import_react29.default.createElement(
|
|
13631
|
+
"button",
|
|
13632
|
+
{
|
|
13633
|
+
onClick: () => handleAddItem("group"),
|
|
13500
13634
|
className: "flex items-center gap-2 px-3 py-1.5 bg-white/5 hover:bg-white/10 border border-white/10 hover:border-white/20 backdrop-blur-sm text-slate-200 hover:text-white rounded-md transition-all duration-200 text-xs font-medium shadow-sm"
|
|
13501
13635
|
},
|
|
13502
13636
|
/* @__PURE__ */ import_react29.default.createElement(import_fi23.FiPlus, { size: 14, className: "text-indigo-400" }),
|
|
13503
13637
|
/* @__PURE__ */ import_react29.default.createElement("span", { className: "hidden sm:inline" }, "Novo Grupo")
|
|
13504
|
-
), /* @__PURE__ */ import_react29.default.createElement(
|
|
13638
|
+
)), /* @__PURE__ */ import_react29.default.createElement(
|
|
13505
13639
|
"button",
|
|
13506
13640
|
{
|
|
13507
13641
|
onClick: onClose,
|
|
@@ -13509,23 +13643,47 @@ function AncestryBoard({
|
|
|
13509
13643
|
},
|
|
13510
13644
|
/* @__PURE__ */ import_react29.default.createElement(import_fi23.FiX, { size: 16 })
|
|
13511
13645
|
))),
|
|
13512
|
-
/* @__PURE__ */ import_react29.default.createElement("div", { className: "
|
|
13513
|
-
|
|
13646
|
+
/* @__PURE__ */ import_react29.default.createElement("div", { className: "h-10 px-6 bg-slate-900/40 border-b border-white/5 flex items-center gap-2 text-[10px] uppercase font-bold tracking-widest text-slate-500 overflow-x-auto no-scrollbar whitespace-nowrap" }, /* @__PURE__ */ import_react29.default.createElement(
|
|
13647
|
+
"button",
|
|
13648
|
+
{
|
|
13649
|
+
onClick: navigateHome,
|
|
13650
|
+
className: `hover:text-white transition-colors flex items-center gap-1 ${navigationStack.length === 0 ? "text-indigo-400" : ""}`
|
|
13651
|
+
},
|
|
13652
|
+
"Home"
|
|
13653
|
+
), currentPath.map((folder, idx) => /* @__PURE__ */ import_react29.default.createElement(import_react29.default.Fragment, { key: folder.id }, /* @__PURE__ */ import_react29.default.createElement(import_fi23.FiChevronRight, { size: 10, className: "shrink-0" }), /* @__PURE__ */ import_react29.default.createElement(
|
|
13654
|
+
"button",
|
|
13655
|
+
{
|
|
13656
|
+
onClick: () => navigateToBreadcrumb(idx),
|
|
13657
|
+
className: `hover:text-white transition-colors ${idx === currentPath.length - 1 ? "text-indigo-400" : ""}`
|
|
13658
|
+
},
|
|
13659
|
+
folder.name || "Sem Nome"
|
|
13660
|
+
))), navigationStack.length > 0 && /* @__PURE__ */ import_react29.default.createElement("div", { className: "ml-auto" }, /* @__PURE__ */ import_react29.default.createElement(
|
|
13661
|
+
"button",
|
|
13662
|
+
{
|
|
13663
|
+
onClick: navigateUp,
|
|
13664
|
+
className: "flex items-center gap-1 px-2 py-1 rounded bg-white/5 hover:bg-white/10 text-slate-400 hover:text-white transition-all"
|
|
13665
|
+
},
|
|
13666
|
+
/* @__PURE__ */ import_react29.default.createElement(import_fi23.FiArrowLeft, { size: 10 }),
|
|
13667
|
+
/* @__PURE__ */ import_react29.default.createElement("span", null, "Voltar")
|
|
13668
|
+
))),
|
|
13669
|
+
/* @__PURE__ */ import_react29.default.createElement("div", { className: "flex-1 overflow-y-auto custom-scrollbar bg-slate-950/30 p-6 space-y-4" }, currentGroups.length === 0 ? /* @__PURE__ */ import_react29.default.createElement("div", { className: "flex flex-col items-center justify-center h-full text-slate-500 gap-3 border-2 border-dashed border-white/5 rounded-xl bg-slate-900/20" }, /* @__PURE__ */ import_react29.default.createElement(import_fi23.FiLayers, { size: 24, className: "opacity-20" }), /* @__PURE__ */ import_react29.default.createElement("p", { className: "text-xs text-center px-4" }, canEdit ? /* @__PURE__ */ import_react29.default.createElement(import_react29.default.Fragment, null, "Esta pasta est\xE1 vazia.", /* @__PURE__ */ import_react29.default.createElement("br", null), "Use os bot\xF5es acima para criar conte\xFAdo.") : /* @__PURE__ */ import_react29.default.createElement(import_react29.default.Fragment, null, "Nenhum conte\xFAdo dispon\xEDvel nesta pasta."))) : currentGroups.map((item, index) => /* @__PURE__ */ import_react29.default.createElement(
|
|
13670
|
+
BoardItem,
|
|
13514
13671
|
{
|
|
13515
|
-
key:
|
|
13672
|
+
key: item.id,
|
|
13516
13673
|
index,
|
|
13517
|
-
|
|
13518
|
-
onUpdate:
|
|
13519
|
-
onDelete:
|
|
13674
|
+
item,
|
|
13675
|
+
onUpdate: handleUpdateItem,
|
|
13676
|
+
onDelete: handleDeleteItem,
|
|
13520
13677
|
onAddSubgroup: handleAddSubgroup,
|
|
13521
13678
|
onIndent: handleIndent,
|
|
13522
13679
|
onOutdent: handleOutdent,
|
|
13523
13680
|
searchItems: allSearchableItems,
|
|
13524
13681
|
onMentionClick: handleMentionClick,
|
|
13682
|
+
onEnterFolder: navigateTo,
|
|
13525
13683
|
canEdit
|
|
13526
13684
|
}
|
|
13527
13685
|
))),
|
|
13528
|
-
/* @__PURE__ */ import_react29.default.createElement("div", { className: "px-5 py-2 border-t border-white/10 bg-slate-950/50 text-xs text-slate-500 flex justify-end flex-shrink-0" }, /* @__PURE__ */ import_react29.default.createElement("span", null,
|
|
13686
|
+
/* @__PURE__ */ import_react29.default.createElement("div", { className: "px-5 py-2 border-t border-white/10 bg-slate-950/50 text-xs text-slate-500 flex justify-end flex-shrink-0" }, /* @__PURE__ */ import_react29.default.createElement("span", null, currentGroups.length, " itens nesta visualiza\xE7\xE3o"))
|
|
13529
13687
|
)
|
|
13530
13688
|
);
|
|
13531
13689
|
}
|
package/dist/index.mjs
CHANGED
|
@@ -12965,7 +12965,9 @@ import {
|
|
|
12965
12965
|
FiX as FiX8,
|
|
12966
12966
|
FiBox as FiBox2,
|
|
12967
12967
|
FiCheckSquare as FiCheckSquare4,
|
|
12968
|
-
FiGitBranch as FiGitBranch4
|
|
12968
|
+
FiGitBranch as FiGitBranch4,
|
|
12969
|
+
FiFolder as FiFolder3,
|
|
12970
|
+
FiChevronRight as FiChevronRight7
|
|
12969
12971
|
} from "react-icons/fi";
|
|
12970
12972
|
import { v4 as uuidv411 } from "uuid";
|
|
12971
12973
|
|
|
@@ -13125,6 +13127,7 @@ function RichGroupEditor({
|
|
|
13125
13127
|
}) {
|
|
13126
13128
|
const editorRef = useRef19(null);
|
|
13127
13129
|
const isMountedRef = useRef19(false);
|
|
13130
|
+
const dismissedTriggerRef = useRef19(null);
|
|
13128
13131
|
const [mentionState, setMentionState] = useState26(null);
|
|
13129
13132
|
const [isEmpty, setIsEmpty] = useState26(true);
|
|
13130
13133
|
useEffect22(() => {
|
|
@@ -13140,10 +13143,17 @@ function RichGroupEditor({
|
|
|
13140
13143
|
const selection = window.getSelection();
|
|
13141
13144
|
if (!selection || selection.rangeCount === 0) return null;
|
|
13142
13145
|
const range = selection.getRangeAt(0);
|
|
13143
|
-
|
|
13144
|
-
|
|
13146
|
+
const node = range.startContainer;
|
|
13147
|
+
if (node.nodeType !== Node.TEXT_NODE) return null;
|
|
13148
|
+
const textBefore = node.textContent.substring(0, range.startOffset);
|
|
13145
13149
|
const atIndex = textBefore.lastIndexOf("@");
|
|
13146
|
-
if (atIndex === -1)
|
|
13150
|
+
if (atIndex === -1) {
|
|
13151
|
+
dismissedTriggerRef.current = null;
|
|
13152
|
+
return null;
|
|
13153
|
+
}
|
|
13154
|
+
if (dismissedTriggerRef.current && dismissedTriggerRef.current.node === node && dismissedTriggerRef.current.atIndex === atIndex) {
|
|
13155
|
+
return null;
|
|
13156
|
+
}
|
|
13147
13157
|
const query = textBefore.substring(atIndex + 1);
|
|
13148
13158
|
if (query.includes(" ") || query.includes(".") || query.includes(",") || query.length > 40) {
|
|
13149
13159
|
return null;
|
|
@@ -13278,6 +13288,21 @@ function RichGroupEditor({
|
|
|
13278
13288
|
const handleBlur = useCallback4(() => {
|
|
13279
13289
|
setTimeout(() => setMentionState(null), 200);
|
|
13280
13290
|
}, []);
|
|
13291
|
+
const handleMentionDismiss = useCallback4(() => {
|
|
13292
|
+
const selection = window.getSelection();
|
|
13293
|
+
if (selection && selection.rangeCount > 0) {
|
|
13294
|
+
const range = selection.getRangeAt(0);
|
|
13295
|
+
const node = range.startContainer;
|
|
13296
|
+
if (node.nodeType === Node.TEXT_NODE) {
|
|
13297
|
+
const textBefore = node.textContent.substring(0, range.startOffset);
|
|
13298
|
+
const atIndex = textBefore.lastIndexOf("@");
|
|
13299
|
+
if (atIndex !== -1) {
|
|
13300
|
+
dismissedTriggerRef.current = { node, atIndex };
|
|
13301
|
+
}
|
|
13302
|
+
}
|
|
13303
|
+
}
|
|
13304
|
+
setMentionState(null);
|
|
13305
|
+
}, []);
|
|
13281
13306
|
return /* @__PURE__ */ React27.createElement("div", { className: "relative" }, isEmpty && !readOnly && /* @__PURE__ */ React27.createElement("div", { className: "absolute top-0 left-0 text-sm text-slate-600 pointer-events-none select-none" }, placeholder || "Escreva sobre este grupo\u2026 ou digite @ para mencionar"), /* @__PURE__ */ React27.createElement(
|
|
13282
13307
|
"div",
|
|
13283
13308
|
{
|
|
@@ -13299,14 +13324,14 @@ function RichGroupEditor({
|
|
|
13299
13324
|
items: searchItems || [],
|
|
13300
13325
|
position: mentionState.position,
|
|
13301
13326
|
onSelect: insertMention,
|
|
13302
|
-
onClose:
|
|
13327
|
+
onClose: handleMentionDismiss
|
|
13303
13328
|
}
|
|
13304
13329
|
));
|
|
13305
13330
|
}
|
|
13306
13331
|
|
|
13307
13332
|
// src/components/AncestryBoard.jsx
|
|
13308
|
-
var
|
|
13309
|
-
|
|
13333
|
+
var BoardItem = ({
|
|
13334
|
+
item,
|
|
13310
13335
|
index,
|
|
13311
13336
|
onUpdate,
|
|
13312
13337
|
onDelete,
|
|
@@ -13315,16 +13340,55 @@ var GroupItem = ({
|
|
|
13315
13340
|
onOutdent,
|
|
13316
13341
|
searchItems,
|
|
13317
13342
|
onMentionClick,
|
|
13343
|
+
onEnterFolder,
|
|
13318
13344
|
canEdit
|
|
13319
13345
|
}) => {
|
|
13346
|
+
var _a;
|
|
13347
|
+
const isFolder = item.type === "folder";
|
|
13320
13348
|
const canIndent = index > 0;
|
|
13321
13349
|
const handleContentChange = (content) => {
|
|
13322
|
-
onUpdate(
|
|
13350
|
+
onUpdate(item.id, { ...item, content });
|
|
13351
|
+
};
|
|
13352
|
+
const handleNameChange = (e) => {
|
|
13353
|
+
onUpdate(item.id, { ...item, name: e.target.value });
|
|
13323
13354
|
};
|
|
13355
|
+
if (isFolder) {
|
|
13356
|
+
return /* @__PURE__ */ React28.createElement("div", { className: "flex flex-col gap-2 mb-3 relative group/item animate-in fade-in slide-in-from-left-2 duration-300" }, /* @__PURE__ */ React28.createElement(
|
|
13357
|
+
"div",
|
|
13358
|
+
{
|
|
13359
|
+
onClick: () => onEnterFolder(item.id),
|
|
13360
|
+
className: "flex items-center gap-3 py-3 px-4 bg-white/5 hover:bg-white/10 border border-white/10 hover:border-white/20 rounded-xl cursor-pointer transition-all duration-200 group/folder"
|
|
13361
|
+
},
|
|
13362
|
+
/* @__PURE__ */ React28.createElement("div", { className: "w-10 h-10 rounded-lg bg-indigo-500/10 flex items-center justify-center text-indigo-400 group-hover/folder:scale-110 transition-transform" }, /* @__PURE__ */ React28.createElement(FiFolder3, { size: 20 })),
|
|
13363
|
+
/* @__PURE__ */ React28.createElement("div", { className: "flex-1 min-w-0" }, canEdit ? /* @__PURE__ */ React28.createElement(
|
|
13364
|
+
"input",
|
|
13365
|
+
{
|
|
13366
|
+
type: "text",
|
|
13367
|
+
value: item.name,
|
|
13368
|
+
onChange: handleNameChange,
|
|
13369
|
+
onClick: (e) => e.stopPropagation(),
|
|
13370
|
+
className: "bg-transparent border-none outline-none text-sm font-medium text-slate-200 w-full focus:text-white",
|
|
13371
|
+
placeholder: "Nome da pasta..."
|
|
13372
|
+
}
|
|
13373
|
+
) : /* @__PURE__ */ React28.createElement("span", { className: "text-sm font-medium text-slate-200" }, item.name), /* @__PURE__ */ React28.createElement("div", { className: "flex items-center gap-2 mt-0.5" }, /* @__PURE__ */ React28.createElement("span", { className: "text-[10px] text-slate-500 uppercase tracking-wider font-semibold" }, ((_a = item.children) == null ? void 0 : _a.length) || 0, " itens"))),
|
|
13374
|
+
/* @__PURE__ */ React28.createElement("div", { className: "flex items-center gap-2 opacity-0 group-hover/folder:opacity-100 transition-opacity" }, canEdit && /* @__PURE__ */ React28.createElement(
|
|
13375
|
+
"button",
|
|
13376
|
+
{
|
|
13377
|
+
onClick: (e) => {
|
|
13378
|
+
e.stopPropagation();
|
|
13379
|
+
onDelete(item.id);
|
|
13380
|
+
},
|
|
13381
|
+
className: "p-1.5 text-slate-500 hover:text-red-400 hover:bg-red-500/10 rounded transition-colors",
|
|
13382
|
+
title: "Remover Pasta"
|
|
13383
|
+
},
|
|
13384
|
+
/* @__PURE__ */ React28.createElement(FiTrash23, { size: 14 })
|
|
13385
|
+
), /* @__PURE__ */ React28.createElement(FiChevronRight7, { className: "text-slate-500 group-hover/folder:translate-x-1 transition-transform" }))
|
|
13386
|
+
));
|
|
13387
|
+
}
|
|
13324
13388
|
return /* @__PURE__ */ React28.createElement("div", { className: "flex flex-col gap-2 mb-3 pl-3 border-l border-white/10 relative group/item animate-in fade-in slide-in-from-left-2 duration-300" }, /* @__PURE__ */ React28.createElement("div", { className: "absolute -left-[1px] top-4 w-2 h-px bg-white/20" }), /* @__PURE__ */ React28.createElement("div", { className: "flex flex-col gap-2 py-2 px-3 transition-all duration-200 hover:bg-white/5 border-l-2 border-transparent hover:border-white/20" }, /* @__PURE__ */ React28.createElement(
|
|
13325
13389
|
RichGroupEditor,
|
|
13326
13390
|
{
|
|
13327
|
-
content:
|
|
13391
|
+
content: item.content,
|
|
13328
13392
|
onChange: handleContentChange,
|
|
13329
13393
|
onMentionClick,
|
|
13330
13394
|
searchItems,
|
|
@@ -13334,15 +13398,25 @@ var GroupItem = ({
|
|
|
13334
13398
|
), canEdit && /* @__PURE__ */ React28.createElement("div", { className: "flex items-center justify-between pt-2 mt-1 border-t border-white/5 opacity-40 group-hover/item:opacity-100 transition-opacity" }, /* @__PURE__ */ React28.createElement("div", { className: "flex items-center gap-1" }, /* @__PURE__ */ React28.createElement(
|
|
13335
13399
|
"button",
|
|
13336
13400
|
{
|
|
13337
|
-
onClick: () => onAddSubgroup(
|
|
13338
|
-
className: "
|
|
13401
|
+
onClick: () => onAddSubgroup(item.id, "group"),
|
|
13402
|
+
className: "flex items-center gap-1 px-1.5 py-1 text-[10px] uppercase font-bold tracking-wider text-slate-500 hover:text-white hover:bg-white/10 rounded transition-colors",
|
|
13339
13403
|
title: "Criar Subgrupo"
|
|
13340
13404
|
},
|
|
13341
|
-
/* @__PURE__ */ React28.createElement(FiPlus10, { size:
|
|
13405
|
+
/* @__PURE__ */ React28.createElement(FiPlus10, { size: 12 }),
|
|
13406
|
+
/* @__PURE__ */ React28.createElement("span", null, "Subgrupo")
|
|
13407
|
+
), /* @__PURE__ */ React28.createElement(
|
|
13408
|
+
"button",
|
|
13409
|
+
{
|
|
13410
|
+
onClick: () => onAddSubgroup(item.id, "folder"),
|
|
13411
|
+
className: "flex items-center gap-1 px-1.5 py-1 text-[10px] uppercase font-bold tracking-wider text-slate-500 hover:text-white hover:bg-white/10 rounded transition-colors",
|
|
13412
|
+
title: "Criar Subpasta"
|
|
13413
|
+
},
|
|
13414
|
+
/* @__PURE__ */ React28.createElement(FiFolder3, { size: 12 }),
|
|
13415
|
+
/* @__PURE__ */ React28.createElement("span", null, "Pasta")
|
|
13342
13416
|
)), /* @__PURE__ */ React28.createElement("div", { className: "flex items-center gap-1" }, /* @__PURE__ */ React28.createElement(
|
|
13343
13417
|
"button",
|
|
13344
13418
|
{
|
|
13345
|
-
onClick: () => onIndent(
|
|
13419
|
+
onClick: () => onIndent(item.id),
|
|
13346
13420
|
disabled: !canIndent,
|
|
13347
13421
|
className: `p-1.5 rounded transition-colors ${!canIndent ? "text-slate-800 cursor-not-allowed" : "text-slate-500 hover:text-white hover:bg-white/10"}`,
|
|
13348
13422
|
title: "Aninhar no grupo acima"
|
|
@@ -13351,7 +13425,7 @@ var GroupItem = ({
|
|
|
13351
13425
|
), /* @__PURE__ */ React28.createElement(
|
|
13352
13426
|
"button",
|
|
13353
13427
|
{
|
|
13354
|
-
onClick: () => onOutdent(
|
|
13428
|
+
onClick: () => onOutdent(item.id),
|
|
13355
13429
|
className: "p-1.5 text-slate-500 hover:text-white hover:bg-white/10 rounded transition-colors",
|
|
13356
13430
|
title: "Desaninhar"
|
|
13357
13431
|
},
|
|
@@ -13359,17 +13433,17 @@ var GroupItem = ({
|
|
|
13359
13433
|
), /* @__PURE__ */ React28.createElement("div", { className: "w-px h-3 bg-white/10 mx-1" }), /* @__PURE__ */ React28.createElement(
|
|
13360
13434
|
"button",
|
|
13361
13435
|
{
|
|
13362
|
-
onClick: () => onDelete(
|
|
13436
|
+
onClick: () => onDelete(item.id),
|
|
13363
13437
|
className: "p-1.5 text-slate-600 hover:text-red-400 hover:bg-red-500/10 rounded transition-colors",
|
|
13364
13438
|
title: "Remover Grupo"
|
|
13365
13439
|
},
|
|
13366
13440
|
/* @__PURE__ */ React28.createElement(FiTrash23, { size: 14 })
|
|
13367
|
-
)))),
|
|
13368
|
-
|
|
13441
|
+
)))), item.children && item.children.length > 0 && /* @__PURE__ */ React28.createElement("div", { className: "ml-2" }, item.children.map((child, idx) => /* @__PURE__ */ React28.createElement(
|
|
13442
|
+
BoardItem,
|
|
13369
13443
|
{
|
|
13370
13444
|
key: child.id,
|
|
13371
13445
|
index: idx,
|
|
13372
|
-
|
|
13446
|
+
item: child,
|
|
13373
13447
|
onUpdate,
|
|
13374
13448
|
onDelete,
|
|
13375
13449
|
onAddSubgroup,
|
|
@@ -13377,6 +13451,7 @@ var GroupItem = ({
|
|
|
13377
13451
|
onOutdent,
|
|
13378
13452
|
searchItems,
|
|
13379
13453
|
onMentionClick,
|
|
13454
|
+
onEnterFolder,
|
|
13380
13455
|
canEdit
|
|
13381
13456
|
}
|
|
13382
13457
|
))));
|
|
@@ -13394,13 +13469,15 @@ function AncestryBoard({
|
|
|
13394
13469
|
const [groups, setGroups] = useState27([]);
|
|
13395
13470
|
const [isLoaded, setIsLoaded] = useState27(false);
|
|
13396
13471
|
const [saveStatus, setSaveStatus] = useState27("idle");
|
|
13472
|
+
const [navigationStack, setNavigationStack] = useState27([]);
|
|
13397
13473
|
const canEdit = useMemo12(() => userRole !== "viewer", [userRole]);
|
|
13398
13474
|
useEffect23(() => {
|
|
13399
13475
|
if (initialGroups && !isLoaded) {
|
|
13400
13476
|
const migrate = (list) => list.map((g) => ({
|
|
13401
13477
|
id: g.id || uuidv411(),
|
|
13478
|
+
type: g.type || "group",
|
|
13479
|
+
name: g.name || "",
|
|
13402
13480
|
content: Array.isArray(g.content) ? g.content : [],
|
|
13403
|
-
// descarta dados legados
|
|
13404
13481
|
children: migrate(g.children || [])
|
|
13405
13482
|
}));
|
|
13406
13483
|
setGroups(migrate(initialGroups));
|
|
@@ -13435,6 +13512,8 @@ function AncestryBoard({
|
|
|
13435
13512
|
}, [availableAncestries, availableNodes]);
|
|
13436
13513
|
const sanitizeGroups = (list) => list.map((g) => ({
|
|
13437
13514
|
id: g.id,
|
|
13515
|
+
type: g.type || "group",
|
|
13516
|
+
name: g.name || "",
|
|
13438
13517
|
content: g.content || [],
|
|
13439
13518
|
children: sanitizeGroups(g.children || [])
|
|
13440
13519
|
}));
|
|
@@ -13465,23 +13544,72 @@ function AncestryBoard({
|
|
|
13465
13544
|
window.addEventListener("keydown", handler);
|
|
13466
13545
|
return () => window.removeEventListener("keydown", handler);
|
|
13467
13546
|
}, [isOpen, onClose]);
|
|
13468
|
-
const
|
|
13469
|
-
|
|
13547
|
+
const currentPath = useMemo12(() => {
|
|
13548
|
+
const path = [];
|
|
13549
|
+
let currentLevel = groups;
|
|
13550
|
+
for (const folderId of navigationStack) {
|
|
13551
|
+
const folder = currentLevel.find((f) => f.id === folderId);
|
|
13552
|
+
if (folder) {
|
|
13553
|
+
path.push(folder);
|
|
13554
|
+
currentLevel = folder.children;
|
|
13555
|
+
}
|
|
13556
|
+
}
|
|
13557
|
+
return path;
|
|
13558
|
+
}, [groups, navigationStack]);
|
|
13559
|
+
const currentGroups = useMemo12(() => {
|
|
13560
|
+
const lastFolder = currentPath[currentPath.length - 1];
|
|
13561
|
+
return lastFolder ? lastFolder.children : groups;
|
|
13562
|
+
}, [groups, currentPath]);
|
|
13563
|
+
const navigateUp = () => {
|
|
13564
|
+
setNavigationStack((prev) => prev.slice(0, -1));
|
|
13565
|
+
};
|
|
13566
|
+
const navigateTo = (folderId) => {
|
|
13567
|
+
setNavigationStack((prev) => [...prev, folderId]);
|
|
13470
13568
|
};
|
|
13471
|
-
const
|
|
13472
|
-
|
|
13569
|
+
const navigateToBreadcrumb = (index) => {
|
|
13570
|
+
setNavigationStack((prev) => prev.slice(0, index + 1));
|
|
13571
|
+
};
|
|
13572
|
+
const navigateHome = () => {
|
|
13573
|
+
setNavigationStack([]);
|
|
13574
|
+
};
|
|
13575
|
+
const handleAddItem = (type = "group") => {
|
|
13576
|
+
const newItem = {
|
|
13577
|
+
id: uuidv411(),
|
|
13578
|
+
type,
|
|
13579
|
+
name: type === "folder" ? "Nova Pasta" : "",
|
|
13580
|
+
content: [],
|
|
13581
|
+
children: []
|
|
13582
|
+
};
|
|
13583
|
+
if (navigationStack.length === 0) {
|
|
13584
|
+
setGroups((prev) => [newItem, ...prev]);
|
|
13585
|
+
} else {
|
|
13586
|
+
const parentId = navigationStack[navigationStack.length - 1];
|
|
13587
|
+
const add = (list) => list.map(
|
|
13588
|
+
(g) => g.id === parentId ? { ...g, children: [newItem, ...g.children] } : { ...g, children: add(g.children) }
|
|
13589
|
+
);
|
|
13590
|
+
setGroups((prev) => add(prev));
|
|
13591
|
+
}
|
|
13592
|
+
};
|
|
13593
|
+
const handleAddSubgroup = (parentId, type = "group") => {
|
|
13594
|
+
const newItem = {
|
|
13595
|
+
id: uuidv411(),
|
|
13596
|
+
type,
|
|
13597
|
+
name: type === "folder" ? "Nova Pasta" : "",
|
|
13598
|
+
content: [],
|
|
13599
|
+
children: []
|
|
13600
|
+
};
|
|
13473
13601
|
const add = (list) => list.map(
|
|
13474
|
-
(g) => g.id === parentId ? { ...g, children: [
|
|
13602
|
+
(g) => g.id === parentId ? { ...g, children: [newItem, ...g.children] } : { ...g, children: add(g.children) }
|
|
13475
13603
|
);
|
|
13476
13604
|
setGroups((prev) => add(prev));
|
|
13477
13605
|
};
|
|
13478
|
-
const
|
|
13606
|
+
const handleUpdateItem = (id, newData) => {
|
|
13479
13607
|
const update = (list) => list.map(
|
|
13480
13608
|
(g) => g.id === id ? newData : { ...g, children: update(g.children) }
|
|
13481
13609
|
);
|
|
13482
13610
|
setGroups((prev) => update(prev));
|
|
13483
13611
|
};
|
|
13484
|
-
const
|
|
13612
|
+
const handleDeleteItem = (id) => {
|
|
13485
13613
|
const del = (list) => list.filter((g) => g.id !== id).map((g) => ({ ...g, children: del(g.children) }));
|
|
13486
13614
|
setGroups((prev) => del(prev));
|
|
13487
13615
|
};
|
|
@@ -13539,15 +13667,23 @@ function AncestryBoard({
|
|
|
13539
13667
|
className: "bg-slate-950 border border-white/10 rounded-xl w-[98vw] h-[97vh] flex flex-col shadow-2xl overflow-hidden animate-in fade-in zoom-in-95 duration-200",
|
|
13540
13668
|
onClick: (e) => e.stopPropagation()
|
|
13541
13669
|
},
|
|
13542
|
-
/* @__PURE__ */ React28.createElement("div", { className: "h-14 px-4 border-b border-white/10 bg-slate-900/90 flex items-center justify-between shrink-0" }, /* @__PURE__ */ React28.createElement("div", { className: "flex items-center gap-4" }, /* @__PURE__ */ React28.createElement("h3", { className: "text-base font-semibold text-white flex items-center gap-2 whitespace-nowrap" }, /* @__PURE__ */ React28.createElement(FiLayers7, { className: "text-indigo-400" }), "Ancestry Board"), saveStatus !== "idle" && /* @__PURE__ */ React28.createElement("div", { className: "flex items-center gap-2 animate-in fade-in slide-in-from-left-2 duration-300" }, /* @__PURE__ */ React28.createElement("div", { className: "w-px h-4 bg-white/10 mx-1" }), /* @__PURE__ */ React28.createElement("div", { className: "flex items-center gap-1.5 px-2 py-0.5 rounded-full bg-slate-900/50 border border-white/5" }, saveStatus === "saving" && /* @__PURE__ */ React28.createElement(React28.Fragment, null, /* @__PURE__ */ React28.createElement(FiLoader5, { className: "animate-spin text-indigo-400", size: 12 }), /* @__PURE__ */ React28.createElement("span", { className: "text-[10px] uppercase tracking-wide font-medium text-indigo-300" }, "Salvando")), saveStatus === "saved" && /* @__PURE__ */ React28.createElement(React28.Fragment, null, /* @__PURE__ */ React28.createElement(FiCheckCircle, { className: "text-emerald-400", size: 12 }), /* @__PURE__ */ React28.createElement("span", { className: "text-[10px] uppercase tracking-wide font-medium text-slate-400" }, "Salvo")), saveStatus === "error" && /* @__PURE__ */ React28.createElement(React28.Fragment, null, /* @__PURE__ */ React28.createElement("span", { className: "w-2 h-2 rounded-full bg-red-500" }), /* @__PURE__ */ React28.createElement("span", { className: "text-[10px] uppercase tracking-wide font-medium text-red-400" }, "Erro"))))), /* @__PURE__ */ React28.createElement("div", { className: "flex items-center gap-3" }, canEdit && /* @__PURE__ */ React28.createElement(
|
|
13670
|
+
/* @__PURE__ */ React28.createElement("div", { className: "h-14 px-4 border-b border-white/10 bg-slate-900/90 flex items-center justify-between shrink-0" }, /* @__PURE__ */ React28.createElement("div", { className: "flex items-center gap-4" }, /* @__PURE__ */ React28.createElement("h3", { className: "text-base font-semibold text-white flex items-center gap-2 whitespace-nowrap" }, /* @__PURE__ */ React28.createElement(FiLayers7, { className: "text-indigo-400" }), "Ancestry Board"), saveStatus !== "idle" && /* @__PURE__ */ React28.createElement("div", { className: "flex items-center gap-2 animate-in fade-in slide-in-from-left-2 duration-300" }, /* @__PURE__ */ React28.createElement("div", { className: "w-px h-4 bg-white/10 mx-1" }), /* @__PURE__ */ React28.createElement("div", { className: "flex items-center gap-1.5 px-2 py-0.5 rounded-full bg-slate-900/50 border border-white/5" }, saveStatus === "saving" && /* @__PURE__ */ React28.createElement(React28.Fragment, null, /* @__PURE__ */ React28.createElement(FiLoader5, { className: "animate-spin text-indigo-400", size: 12 }), /* @__PURE__ */ React28.createElement("span", { className: "text-[10px] uppercase tracking-wide font-medium text-indigo-300" }, "Salvando")), saveStatus === "saved" && /* @__PURE__ */ React28.createElement(React28.Fragment, null, /* @__PURE__ */ React28.createElement(FiCheckCircle, { className: "text-emerald-400", size: 12 }), /* @__PURE__ */ React28.createElement("span", { className: "text-[10px] uppercase tracking-wide font-medium text-slate-400" }, "Salvo")), saveStatus === "error" && /* @__PURE__ */ React28.createElement(React28.Fragment, null, /* @__PURE__ */ React28.createElement("span", { className: "w-2 h-2 rounded-full bg-red-500" }), /* @__PURE__ */ React28.createElement("span", { className: "text-[10px] uppercase tracking-wide font-medium text-red-400" }, "Erro"))))), /* @__PURE__ */ React28.createElement("div", { className: "flex items-center gap-3" }, canEdit && /* @__PURE__ */ React28.createElement(React28.Fragment, null, /* @__PURE__ */ React28.createElement(
|
|
13543
13671
|
"button",
|
|
13544
13672
|
{
|
|
13545
|
-
onClick:
|
|
13673
|
+
onClick: () => handleAddItem("folder"),
|
|
13674
|
+
className: "flex items-center gap-2 px-3 py-1.5 bg-white/5 hover:bg-white/10 border border-white/10 hover:border-white/20 backdrop-blur-sm text-slate-200 hover:text-white rounded-md transition-all duration-200 text-xs font-medium shadow-sm"
|
|
13675
|
+
},
|
|
13676
|
+
/* @__PURE__ */ React28.createElement(FiPlus10, { size: 14, className: "text-amber-400" }),
|
|
13677
|
+
/* @__PURE__ */ React28.createElement("span", { className: "hidden sm:inline" }, "Nova Pasta")
|
|
13678
|
+
), /* @__PURE__ */ React28.createElement(
|
|
13679
|
+
"button",
|
|
13680
|
+
{
|
|
13681
|
+
onClick: () => handleAddItem("group"),
|
|
13546
13682
|
className: "flex items-center gap-2 px-3 py-1.5 bg-white/5 hover:bg-white/10 border border-white/10 hover:border-white/20 backdrop-blur-sm text-slate-200 hover:text-white rounded-md transition-all duration-200 text-xs font-medium shadow-sm"
|
|
13547
13683
|
},
|
|
13548
13684
|
/* @__PURE__ */ React28.createElement(FiPlus10, { size: 14, className: "text-indigo-400" }),
|
|
13549
13685
|
/* @__PURE__ */ React28.createElement("span", { className: "hidden sm:inline" }, "Novo Grupo")
|
|
13550
|
-
), /* @__PURE__ */ React28.createElement(
|
|
13686
|
+
)), /* @__PURE__ */ React28.createElement(
|
|
13551
13687
|
"button",
|
|
13552
13688
|
{
|
|
13553
13689
|
onClick: onClose,
|
|
@@ -13555,23 +13691,47 @@ function AncestryBoard({
|
|
|
13555
13691
|
},
|
|
13556
13692
|
/* @__PURE__ */ React28.createElement(FiX8, { size: 16 })
|
|
13557
13693
|
))),
|
|
13558
|
-
/* @__PURE__ */ React28.createElement("div", { className: "
|
|
13559
|
-
|
|
13694
|
+
/* @__PURE__ */ React28.createElement("div", { className: "h-10 px-6 bg-slate-900/40 border-b border-white/5 flex items-center gap-2 text-[10px] uppercase font-bold tracking-widest text-slate-500 overflow-x-auto no-scrollbar whitespace-nowrap" }, /* @__PURE__ */ React28.createElement(
|
|
13695
|
+
"button",
|
|
13696
|
+
{
|
|
13697
|
+
onClick: navigateHome,
|
|
13698
|
+
className: `hover:text-white transition-colors flex items-center gap-1 ${navigationStack.length === 0 ? "text-indigo-400" : ""}`
|
|
13699
|
+
},
|
|
13700
|
+
"Home"
|
|
13701
|
+
), currentPath.map((folder, idx) => /* @__PURE__ */ React28.createElement(React28.Fragment, { key: folder.id }, /* @__PURE__ */ React28.createElement(FiChevronRight7, { size: 10, className: "shrink-0" }), /* @__PURE__ */ React28.createElement(
|
|
13702
|
+
"button",
|
|
13703
|
+
{
|
|
13704
|
+
onClick: () => navigateToBreadcrumb(idx),
|
|
13705
|
+
className: `hover:text-white transition-colors ${idx === currentPath.length - 1 ? "text-indigo-400" : ""}`
|
|
13706
|
+
},
|
|
13707
|
+
folder.name || "Sem Nome"
|
|
13708
|
+
))), navigationStack.length > 0 && /* @__PURE__ */ React28.createElement("div", { className: "ml-auto" }, /* @__PURE__ */ React28.createElement(
|
|
13709
|
+
"button",
|
|
13710
|
+
{
|
|
13711
|
+
onClick: navigateUp,
|
|
13712
|
+
className: "flex items-center gap-1 px-2 py-1 rounded bg-white/5 hover:bg-white/10 text-slate-400 hover:text-white transition-all"
|
|
13713
|
+
},
|
|
13714
|
+
/* @__PURE__ */ React28.createElement(FiArrowLeft3, { size: 10 }),
|
|
13715
|
+
/* @__PURE__ */ React28.createElement("span", null, "Voltar")
|
|
13716
|
+
))),
|
|
13717
|
+
/* @__PURE__ */ React28.createElement("div", { className: "flex-1 overflow-y-auto custom-scrollbar bg-slate-950/30 p-6 space-y-4" }, currentGroups.length === 0 ? /* @__PURE__ */ React28.createElement("div", { className: "flex flex-col items-center justify-center h-full text-slate-500 gap-3 border-2 border-dashed border-white/5 rounded-xl bg-slate-900/20" }, /* @__PURE__ */ React28.createElement(FiLayers7, { size: 24, className: "opacity-20" }), /* @__PURE__ */ React28.createElement("p", { className: "text-xs text-center px-4" }, canEdit ? /* @__PURE__ */ React28.createElement(React28.Fragment, null, "Esta pasta est\xE1 vazia.", /* @__PURE__ */ React28.createElement("br", null), "Use os bot\xF5es acima para criar conte\xFAdo.") : /* @__PURE__ */ React28.createElement(React28.Fragment, null, "Nenhum conte\xFAdo dispon\xEDvel nesta pasta."))) : currentGroups.map((item, index) => /* @__PURE__ */ React28.createElement(
|
|
13718
|
+
BoardItem,
|
|
13560
13719
|
{
|
|
13561
|
-
key:
|
|
13720
|
+
key: item.id,
|
|
13562
13721
|
index,
|
|
13563
|
-
|
|
13564
|
-
onUpdate:
|
|
13565
|
-
onDelete:
|
|
13722
|
+
item,
|
|
13723
|
+
onUpdate: handleUpdateItem,
|
|
13724
|
+
onDelete: handleDeleteItem,
|
|
13566
13725
|
onAddSubgroup: handleAddSubgroup,
|
|
13567
13726
|
onIndent: handleIndent,
|
|
13568
13727
|
onOutdent: handleOutdent,
|
|
13569
13728
|
searchItems: allSearchableItems,
|
|
13570
13729
|
onMentionClick: handleMentionClick,
|
|
13730
|
+
onEnterFolder: navigateTo,
|
|
13571
13731
|
canEdit
|
|
13572
13732
|
}
|
|
13573
13733
|
))),
|
|
13574
|
-
/* @__PURE__ */ React28.createElement("div", { className: "px-5 py-2 border-t border-white/10 bg-slate-950/50 text-xs text-slate-500 flex justify-end flex-shrink-0" }, /* @__PURE__ */ React28.createElement("span", null,
|
|
13734
|
+
/* @__PURE__ */ React28.createElement("div", { className: "px-5 py-2 border-t border-white/10 bg-slate-950/50 text-xs text-slate-500 flex justify-end flex-shrink-0" }, /* @__PURE__ */ React28.createElement("span", null, currentGroups.length, " itens nesta visualiza\xE7\xE3o"))
|
|
13575
13735
|
)
|
|
13576
13736
|
);
|
|
13577
13737
|
}
|
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.
|
|
3
|
+
"version": "1.2.6-dev.14",
|
|
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",
|