@pagamio/frontend-commons-lib 0.8.218 → 0.8.219
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.
|
@@ -6,16 +6,34 @@ import { useAppSidebarContext } from '../../context';
|
|
|
6
6
|
import { useLibTranslations, useTranslation } from '../../translations';
|
|
7
7
|
const AppSidebarMenu = () => {
|
|
8
8
|
const { pages, groupByItem } = useAppSidebarContext();
|
|
9
|
+
// Helper to generate unique key for sidebar items
|
|
10
|
+
const getItemKey = (item, index) => item.key || item.href || `${item.label}-${index}`;
|
|
9
11
|
// If groupByItem is true each item gets its own ItemGroup
|
|
10
12
|
if (groupByItem) {
|
|
11
|
-
return (_jsx(Sidebar.Items, { children: pages.map((item) =>
|
|
13
|
+
return (_jsx(Sidebar.Items, { children: pages.map((item, index) => {
|
|
14
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
15
|
+
const { key, ...itemProps } = item;
|
|
16
|
+
return (_jsx(Sidebar.ItemGroup, { children: _jsx(AppSidebarItem, { ...itemProps }) }, getItemKey(item, index)));
|
|
17
|
+
}) }));
|
|
12
18
|
}
|
|
13
19
|
// Default behavior all items in one ItemGroup
|
|
14
|
-
return (_jsx(Sidebar.Items, { children: _jsx(Sidebar.ItemGroup, { children: pages.map((item) =>
|
|
20
|
+
return (_jsx(Sidebar.Items, { children: _jsx(Sidebar.ItemGroup, { children: pages.map((item, index) => {
|
|
21
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
22
|
+
const { key, ...itemProps } = item;
|
|
23
|
+
return _jsx(AppSidebarItem, { ...itemProps }, getItemKey(item, index));
|
|
24
|
+
}) }) }));
|
|
15
25
|
};
|
|
16
|
-
const AppSidebarItem = ({ href, target, icon, label, items, badge, forceDropdown, collapsible = true, showSeparator = false, }) => {
|
|
17
|
-
const { pathname, linkComponent: Link } = useAppSidebarContext();
|
|
26
|
+
const AppSidebarItem = ({ href, target, icon, label, items, badge, forceDropdown, collapsible = true, showSeparator = false, isSectionHeader = false, }) => {
|
|
27
|
+
const { pathname, linkComponent: Link, desktop } = useAppSidebarContext();
|
|
18
28
|
const { t } = useTranslation();
|
|
29
|
+
// Handle section headers - render as a non-interactive label (hide when collapsed)
|
|
30
|
+
if (isSectionHeader) {
|
|
31
|
+
// Don't render section headers when sidebar is collapsed
|
|
32
|
+
if (desktop.isCollapsed) {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
return (_jsxs("div", { className: "pt-4 pb-1 px-3 first:pt-0", children: [showSeparator && _jsx("hr", { className: "mb-3 border-t border-gray-200 dark:border-gray-700" }), _jsx("span", { className: "text-xs font-semibold uppercase tracking-wider text-gray-500 dark:text-gray-400", children: t(label) })] }));
|
|
36
|
+
}
|
|
19
37
|
// Check if current path matches this item or any of its descendants
|
|
20
38
|
const isParentActive = href ? pathname === href || pathname.startsWith(`${href}/`) : false;
|
|
21
39
|
// Recursive function to check if any nested child is active
|
|
@@ -40,14 +58,18 @@ const AppSidebarItem = ({ href, target, icon, label, items, badge, forceDropdown
|
|
|
40
58
|
}
|
|
41
59
|
// Handle non-collapsible sections - render as section header with always-visible links
|
|
42
60
|
if (items?.length && collapsible === false) {
|
|
43
|
-
return (_jsxs("div", { className: "space-y-1", children: [showSeparator && _jsx("hr", { className: "my-3 border-t border-gray-200 dark:border-gray-700" }), _jsxs("div", { className: twMerge('flex items-center gap-3 px-3 py-2 text-sm font-semibold uppercase tracking-wider', 'text-gray-500 dark:text-gray-400'), children: [icon && React.createElement(icon, { className: 'h-5 w-5' }), _jsx("span", { children: t(label) })] }), _jsx("div", { className: "space-y-1 pl-2", children: items.map((child) =>
|
|
44
|
-
//
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
61
|
+
return (_jsxs("div", { className: "space-y-1", children: [showSeparator && _jsx("hr", { className: "my-3 border-t border-gray-200 dark:border-gray-700" }), _jsxs("div", { className: twMerge('flex items-center gap-3 px-3 py-2 text-sm font-semibold uppercase tracking-wider', 'text-gray-500 dark:text-gray-400'), children: [icon && React.createElement(icon, { className: 'h-5 w-5' }), _jsx("span", { children: t(label) })] }), _jsx("div", { className: "space-y-1 pl-2", children: items.map((child, childIndex) => {
|
|
62
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
63
|
+
const { key, ...childProps } = child;
|
|
64
|
+
return (_jsx(React.Fragment, { children: child.items?.length ? (
|
|
65
|
+
// Recursively render nested items
|
|
66
|
+
_jsx("div", { className: "pl-3", children: _jsx(AppSidebarItem, { ...childProps }) })) : child.href ? (
|
|
67
|
+
// Render leaf item with link
|
|
68
|
+
_jsx(Sidebar.Item, { as: Link, href: child.href, target: child.target, icon: child.icon, className: twMerge('justify-center [&>*]:font-normal', 'text-gray-600 hover:text-primary-700 hover:bg-primary-50/70', 'dark:text-gray-400 dark:hover:text-primary-300 dark:hover:bg-primary-900/30', pathname === child.href &&
|
|
69
|
+
'text-primary-700 bg-primary-100/80 hover:bg-primary-100/80 dark:text-primary-300 dark:bg-primary-900/40 dark:hover:bg-primary-900/40'), children: t(child.label) })) : (
|
|
70
|
+
// Render leaf item without link
|
|
71
|
+
_jsx(Sidebar.Item, { icon: child.icon, className: twMerge('justify-center [&>*]:font-normal', 'text-gray-600 cursor-default', 'dark:text-gray-400'), children: t(child.label) })) }, child.key || child.href || `${child.label}-${childIndex}`));
|
|
72
|
+
}) })] }));
|
|
51
73
|
}
|
|
52
74
|
if (items?.length) {
|
|
53
75
|
const isOpen = isParentActive || hasActiveChild(items);
|
|
@@ -57,14 +79,18 @@ const AppSidebarItem = ({ href, target, icon, label, items, badge, forceDropdown
|
|
|
57
79
|
// Parent styling when it's directly active
|
|
58
80
|
isParentActive &&
|
|
59
81
|
!hasActiveChild(items) &&
|
|
60
|
-
'text-primary-700 bg-primary-100/80 hover:bg-primary-100/80 dark:text-primary-300 dark:bg-primary-900/40 dark:hover:bg-primary-900/40'), theme: { list: 'space-y-2 py-2 [&>li>div]:w-full' }, children: items.map((child) =>
|
|
61
|
-
//
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
82
|
+
'text-primary-700 bg-primary-100/80 hover:bg-primary-100/80 dark:text-primary-300 dark:bg-primary-900/40 dark:hover:bg-primary-900/40'), theme: { list: 'space-y-2 py-2 [&>li>div]:w-full' }, children: items.map((child, childIndex) => {
|
|
83
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
84
|
+
const { key, ...childProps } = child;
|
|
85
|
+
return (_jsx(React.Fragment, { children: child.items?.length ? (
|
|
86
|
+
// Recursively render nested items
|
|
87
|
+
_jsx("div", { className: "pl-3", children: _jsx(AppSidebarItem, { ...childProps }) })) : child.href ? (
|
|
88
|
+
// Render leaf item with link
|
|
89
|
+
_jsx(Sidebar.Item, { as: Link, href: child.href, target: child.target, icon: child.icon, className: twMerge('justify-center [&>*]:font-normal', 'text-gray-600 hover:text-primary-700 hover:bg-primary-50/70', 'dark:text-gray-400 dark:hover:text-primary-300 dark:hover:bg-primary-900/30', pathname === child.href &&
|
|
90
|
+
'text-primary-700 bg-primary-100/80 hover:bg-primary-100/80 dark:text-primary-300 dark:bg-primary-900/40 dark:hover:bg-primary-900/40'), children: t(child.label) })) : (
|
|
91
|
+
// Render leaf item without link
|
|
92
|
+
_jsx(Sidebar.Item, { icon: child.icon, className: twMerge('justify-center [&>*]:font-normal', 'text-gray-600 cursor-default', 'dark:text-gray-400'), children: t(child.label) })) }, child.key || child.href || `${child.label}-${childIndex}`));
|
|
93
|
+
}) }));
|
|
68
94
|
}
|
|
69
95
|
// Render leaf item
|
|
70
96
|
// If no href, render as a non-link item
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import type { FC, HTMLAttributeAnchorTarget, PropsWithChildren } from 'react';
|
|
2
2
|
import React from 'react';
|
|
3
3
|
interface AppSidebarPageItem {
|
|
4
|
+
/**
|
|
5
|
+
* Unique key for React rendering. If not provided, href or label will be used.
|
|
6
|
+
* Use this to avoid duplicate key warnings when multiple items have the same label.
|
|
7
|
+
*/
|
|
8
|
+
key?: string;
|
|
4
9
|
href?: string;
|
|
5
10
|
target?: HTMLAttributeAnchorTarget;
|
|
6
11
|
icon?: FC;
|
|
@@ -19,6 +24,11 @@ interface AppSidebarPageItem {
|
|
|
19
24
|
* Useful for visually separating non-collapsible sections. Defaults to false.
|
|
20
25
|
*/
|
|
21
26
|
showSeparator?: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* When true, renders as a flat inline section header (non-interactive label).
|
|
29
|
+
* Used for visual grouping in flat sidebar structures.
|
|
30
|
+
*/
|
|
31
|
+
isSectionHeader?: boolean;
|
|
22
32
|
}
|
|
23
33
|
/**
|
|
24
34
|
* Props for the AppSidebarContext
|
package/lib/styles.css
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pagamio/frontend-commons-lib",
|
|
3
3
|
"description": "Pagamio library for Frontend reusable components like the form engine and table container",
|
|
4
|
-
"version": "0.8.
|
|
4
|
+
"version": "0.8.219",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public",
|
|
7
7
|
"provenance": false
|