@tree-ia/design-system 2.4.1 → 2.5.0

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/README.md CHANGED
File without changes
package/dist/index.d.mts CHANGED
File without changes
File without changes
package/dist/index.mjs CHANGED
@@ -1261,11 +1261,22 @@ function Sidebar({ menuItems, logo, collapsedLogo, currentPath, linkComponent: L
1261
1261
  const [expandedIds, setExpandedIds] = useState(() => new Set(defaultExpandedIds ?? []));
1262
1262
  const cubicBezier = "cubic-bezier(0.4, 0, 0.2, 1)";
1263
1263
  useEffect(() => {
1264
- const idsToExpand = [];
1265
- for (const item of menuItems) if (item.children?.some((c) => currentPath === c.href)) idsToExpand.push(item.id);
1266
- if (footerItems) {
1267
- for (const item of footerItems) if (item.children?.some((c) => currentPath === c.href)) idsToExpand.push(item.id);
1264
+ function collectAncestorsOfActive(items, acc) {
1265
+ let hasActive = false;
1266
+ for (const item of items) {
1267
+ const selfActive = currentPath === item.href;
1268
+ let childActive = false;
1269
+ if (item.children && item.children.length > 0) {
1270
+ childActive = collectAncestorsOfActive(item.children, acc);
1271
+ if (childActive) acc.push(item.id);
1272
+ }
1273
+ if (selfActive || childActive) hasActive = true;
1274
+ }
1275
+ return hasActive;
1268
1276
  }
1277
+ const idsToExpand = [];
1278
+ collectAncestorsOfActive(menuItems, idsToExpand);
1279
+ if (footerItems) collectAncestorsOfActive(footerItems, idsToExpand);
1269
1280
  if (idsToExpand.length > 0) setExpandedIds((prev) => {
1270
1281
  const next = new Set(prev);
1271
1282
  for (const id of idsToExpand) next.add(id);
@@ -1293,6 +1304,27 @@ function Sidebar({ menuItems, logo, collapsedLogo, currentPath, linkComponent: L
1293
1304
  return next;
1294
1305
  });
1295
1306
  }
1307
+ /** True if the item or any nested descendant matches `currentPath`. */
1308
+ function hasActiveDescendant(item) {
1309
+ if (!item.children) return false;
1310
+ for (const child of item.children) {
1311
+ if (currentPath === child.href) return true;
1312
+ if (hasActiveDescendant(child)) return true;
1313
+ }
1314
+ return false;
1315
+ }
1316
+ /**
1317
+ * Counts how many descendant nodes are currently visible in the tree —
1318
+ * i.e. all direct children, plus all descendants of expanded children
1319
+ * (recurse only inside expanded branches). Used to size the `maxHeight`
1320
+ * animation container so it fits the full expanded subtree.
1321
+ */
1322
+ function countVisibleDescendants(item) {
1323
+ if (!item.children || item.children.length === 0) return 0;
1324
+ let count = item.children.length;
1325
+ for (const child of item.children) if (expandedIds.has(child.id)) count += countVisibleDescendants(child);
1326
+ return count;
1327
+ }
1296
1328
  /** Group items by section — items before any section go into a null group */
1297
1329
  function groupBySection(items) {
1298
1330
  const groups = [];
@@ -1316,6 +1348,57 @@ function Sidebar({ menuItems, logo, collapsedLogo, currentPath, linkComponent: L
1316
1348
  children: section
1317
1349
  });
1318
1350
  }
1351
+ /**
1352
+ * Renders a nested child item (inside an expanded parent). Recursive —
1353
+ * if the child itself has `children`, renders as expandable and recurses.
1354
+ * Styling is the "child" variant (smaller icon, smaller text, indented)
1355
+ * regardless of depth, to keep visual consistency across levels.
1356
+ */
1357
+ function renderChildItem(item) {
1358
+ const ChildIcon = item.icon;
1359
+ const childActive = currentPath === item.href;
1360
+ const hasChildren = item.children && item.children.length > 0;
1361
+ const isExpanded = expandedIds.has(item.id);
1362
+ const isChildActive = hasActiveDescendant(item);
1363
+ if (hasChildren) return /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsxs("button", {
1364
+ onClick: () => toggleExpand(item.id),
1365
+ className: cn$29("w-full flex items-center pl-4 pr-4 py-2 rounded-r-lg text-[13px] cursor-pointer", childActive || isChildActive ? "text-[var(--dashboard-sidebar-active-text,#ff521d)] font-semibold" : "text-[var(--dashboard-sidebar-text,#403f52)] hover:text-[var(--dashboard-sidebar-active-text,#ff521d)]"),
1366
+ style: { transition: "background-color 200ms, color 200ms" },
1367
+ children: [
1368
+ /* @__PURE__ */ jsx(ChildIcon, {
1369
+ size: 15,
1370
+ className: "mr-2 flex-shrink-0"
1371
+ }),
1372
+ /* @__PURE__ */ jsx("span", {
1373
+ className: "whitespace-nowrap flex-1 text-left",
1374
+ children: item.label
1375
+ }),
1376
+ /* @__PURE__ */ jsx(ChevronRight, {
1377
+ size: 12,
1378
+ className: cn$29("ml-auto flex-shrink-0 transition-transform duration-200", isExpanded ? "rotate-90" : "")
1379
+ })
1380
+ ]
1381
+ }), /* @__PURE__ */ jsx("div", {
1382
+ className: "overflow-hidden transition-all duration-200 ml-4 border-l-2 border-[var(--dashboard-sidebar-border,#e0dfe3)]",
1383
+ style: { maxHeight: isExpanded ? `${countVisibleDescendants(item) * 40}px` : "0" },
1384
+ children: item.children.map((grandchild) => renderChildItem(grandchild))
1385
+ })] }, item.id);
1386
+ return /* @__PURE__ */ jsx(LinkComponent, {
1387
+ href: item.href,
1388
+ className: "block",
1389
+ children: /* @__PURE__ */ jsxs("div", {
1390
+ className: cn$29("w-full flex items-center pl-4 pr-4 py-2 rounded-r-lg text-[13px] cursor-pointer", childActive ? "text-[var(--dashboard-sidebar-active-text,#ff521d)] font-semibold border-l-2 border-[var(--dashboard-primary,#ff521d)] -ml-[2px]" : "text-[var(--dashboard-sidebar-text,#403f52)] hover:text-[var(--dashboard-sidebar-active-text,#ff521d)]"),
1391
+ style: { transition: "background-color 200ms, color 200ms" },
1392
+ children: [/* @__PURE__ */ jsx(ChildIcon, {
1393
+ size: 15,
1394
+ className: "mr-2 flex-shrink-0"
1395
+ }), /* @__PURE__ */ jsx("span", {
1396
+ className: "whitespace-nowrap",
1397
+ children: item.label
1398
+ })]
1399
+ })
1400
+ }, item.id);
1401
+ }
1319
1402
  function renderMenuItem(item, collapsed, mobile) {
1320
1403
  const Icon = item.icon;
1321
1404
  const hasChildren = item.children && item.children.length > 0;
@@ -1343,26 +1426,8 @@ function Sidebar({ menuItems, logo, collapsedLogo, currentPath, linkComponent: L
1343
1426
  })] })]
1344
1427
  }), (!collapsed || mobile) && /* @__PURE__ */ jsx("div", {
1345
1428
  className: "overflow-hidden transition-all duration-200 ml-7 border-l-2 border-[var(--dashboard-sidebar-border,#e0dfe3)]",
1346
- style: { maxHeight: isExpanded ? `${item.children.length * 40}px` : "0" },
1347
- children: item.children.map((child) => {
1348
- const ChildIcon = child.icon;
1349
- const childActive = currentPath === child.href;
1350
- return /* @__PURE__ */ jsx(LinkComponent, {
1351
- href: child.href,
1352
- className: "block",
1353
- children: /* @__PURE__ */ jsxs("div", {
1354
- className: cn$29("w-full flex items-center pl-4 pr-4 py-2 rounded-r-lg text-[13px] cursor-pointer", childActive ? "text-[var(--dashboard-sidebar-active-text,#ff521d)] font-semibold border-l-2 border-[var(--dashboard-primary,#ff521d)] -ml-[2px]" : "text-[var(--dashboard-sidebar-text,#403f52)] hover:text-[var(--dashboard-sidebar-active-text,#ff521d)]"),
1355
- style: { transition: "background-color 200ms, color 200ms" },
1356
- children: [/* @__PURE__ */ jsx(ChildIcon, {
1357
- size: 15,
1358
- className: "mr-2 flex-shrink-0"
1359
- }), /* @__PURE__ */ jsx("span", {
1360
- className: "whitespace-nowrap",
1361
- children: child.label
1362
- })]
1363
- })
1364
- }, child.id);
1365
- })
1429
+ style: { maxHeight: isExpanded ? `${countVisibleDescendants(item) * 40}px` : "0" },
1430
+ children: item.children.map((child) => renderChildItem(child))
1366
1431
  })] }, item.id);
1367
1432
  return /* @__PURE__ */ jsx(LinkComponent, {
1368
1433
  href: item.href,