@veiag/payload-enhanced-sidebar 0.1.0 → 0.1.1
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 +18 -2
- package/dist/components/EnhancedSidebar/SidebarContent.js +3 -0
- package/dist/components/EnhancedSidebar/SidebarContent.js.map +1 -1
- package/dist/components/EnhancedSidebar/TabsBar/index.js +2 -1
- package/dist/components/EnhancedSidebar/TabsBar/index.js.map +1 -1
- package/dist/components/EnhancedSidebar/index.client.js +14 -8
- package/dist/components/EnhancedSidebar/index.client.js.map +1 -1
- package/dist/types.d.ts +26 -9
- package/dist/types.js.map +1 -1
- package/package.json +1 -1
- package/dist/components/EnhancedSidebar/CustomTabContent.d.ts +0 -6
- package/dist/components/EnhancedSidebar/CustomTabContent.js +0 -35
- package/dist/components/EnhancedSidebar/CustomTabContent.js.map +0 -1
package/README.md
CHANGED
|
@@ -73,6 +73,15 @@ export default buildConfig({
|
|
|
73
73
|
label: { en: 'Content', uk: 'Контент' },
|
|
74
74
|
collections: ['posts', 'pages', 'categories'],
|
|
75
75
|
},
|
|
76
|
+
// Link to external documentation
|
|
77
|
+
{
|
|
78
|
+
id: 'docs',
|
|
79
|
+
type: 'link',
|
|
80
|
+
href: 'https://payloadcms.com/',
|
|
81
|
+
icon: 'BookOpen',
|
|
82
|
+
isExternal: true,
|
|
83
|
+
label: { en: 'Documentation', uk: 'Документація' },
|
|
84
|
+
},
|
|
76
85
|
// E-commerce tab with custom items
|
|
77
86
|
{
|
|
78
87
|
id: 'ecommerce',
|
|
@@ -104,6 +113,12 @@ export default buildConfig({
|
|
|
104
113
|
label: { en: 'API Keys', uk: 'API Ключі' },
|
|
105
114
|
// No group - will appear at the bottom
|
|
106
115
|
},
|
|
116
|
+
{
|
|
117
|
+
slug:'external-link',
|
|
118
|
+
href: 'https://example.com',
|
|
119
|
+
isExternal: true,
|
|
120
|
+
label: { en: 'External Link', uk: 'Зовнішнє Посилання'}
|
|
121
|
+
}
|
|
107
122
|
],
|
|
108
123
|
},
|
|
109
124
|
],
|
|
@@ -146,7 +161,8 @@ Array of tabs and links to show in the sidebar.
|
|
|
146
161
|
| `type` | `'link'` | Yes | Link type |
|
|
147
162
|
| `icon` | `string` | Yes | Lucide icon name |
|
|
148
163
|
| `label` | `LocalizedString` | Yes | Link tooltip/label |
|
|
149
|
-
| `href` | `string` | Yes | URL
|
|
164
|
+
| `href` | `string` | Yes | URL |
|
|
165
|
+
| `isExternal` | `boolean` | No | If true, `href` is absolute URL, if not, `href` is relative to admin route |
|
|
150
166
|
|
|
151
167
|
### `customItems`
|
|
152
168
|
|
|
@@ -155,7 +171,7 @@ Custom items can be added to any tab:
|
|
|
155
171
|
```typescript
|
|
156
172
|
{
|
|
157
173
|
slug: 'unique-slug', // Required: unique identifier
|
|
158
|
-
href: '/path', // Required: URL
|
|
174
|
+
href: '/path', // Required: URL
|
|
159
175
|
label: { en: 'Label' }, // Required: display label
|
|
160
176
|
group: { en: 'Group Name' }, // Optional: merge into existing group or create new
|
|
161
177
|
isExternal: true, // Optional: if true, href is absolute URL
|
|
@@ -72,6 +72,7 @@ export const SidebarContent = ({ afterNavLinks, beforeNavLinks, groups, navPrefe
|
|
|
72
72
|
slug: item.slug,
|
|
73
73
|
type: 'custom',
|
|
74
74
|
href: item.href,
|
|
75
|
+
isExternal: item.isExternal,
|
|
75
76
|
label: item.label
|
|
76
77
|
});
|
|
77
78
|
} else {
|
|
@@ -82,6 +83,7 @@ export const SidebarContent = ({ afterNavLinks, beforeNavLinks, groups, navPrefe
|
|
|
82
83
|
slug: item.slug,
|
|
83
84
|
type: 'custom',
|
|
84
85
|
href: item.href,
|
|
86
|
+
isExternal: item.isExternal,
|
|
85
87
|
label: item.label
|
|
86
88
|
}
|
|
87
89
|
],
|
|
@@ -99,6 +101,7 @@ export const SidebarContent = ({ afterNavLinks, beforeNavLinks, groups, navPrefe
|
|
|
99
101
|
slug: item.slug,
|
|
100
102
|
type: 'custom',
|
|
101
103
|
href: item.href,
|
|
104
|
+
isExternal: item.isExternal,
|
|
102
105
|
label: item.label
|
|
103
106
|
})),
|
|
104
107
|
label: ''
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/EnhancedSidebar/SidebarContent.tsx"],"sourcesContent":["'use client'\nimport type { NavPreferences } from 'payload'\n\nimport { useTranslation } from '@payloadcms/ui'\nimport React, { useEffect, useMemo, useState } from 'react'\n\nimport type {\n EnhancedSidebarConfig,\n ExtendedGroup,\n SidebarTabContent as SidebarTabContentType,\n SidebarTabItem,\n} from '../../types'\n\nimport { extractLocalizedValue } from '../../utils'\nimport { EnhancedSidebarClient } from './index.client'\nimport { SidebarWrapper } from './SidebarWrapper'\nimport { TabsBar } from './TabsBar'\n\nconst baseClass = 'enhanced-sidebar'\n\nexport type SidebarContentProps = {\n afterNavLinks?: React.ReactNode\n beforeNavLinks?: React.ReactNode\n groups: ExtendedGroup[]\n navPreferences: NavPreferences | null\n sidebarConfig: EnhancedSidebarConfig\n}\n\nconst STORAGE_KEY = 'payload-enhanced-sidebar-active-tab'\n\nexport const SidebarContent: React.FC<SidebarContentProps> = ({\n afterNavLinks,\n beforeNavLinks,\n groups,\n navPreferences,\n sidebarConfig,\n}) => {\n const { i18n } = useTranslation()\n const currentLang = i18n.language\n\n const tabs = sidebarConfig.tabs?.filter((t): t is SidebarTabContentType => t.type === 'tab') ?? []\n const defaultTabId = tabs[0]?.id ?? 'default'\n\n // Always start with default to match server render\n const [activeTabId, setActiveTabId] = useState(defaultTabId)\n\n // Read from localStorage only after hydration\n useEffect(() => {\n const stored = localStorage.getItem(STORAGE_KEY)\n if (stored && tabs.some((t) => t.id === stored)) {\n setActiveTabId(stored)\n }\n }, []) // eslint-disable-line react-hooks/exhaustive-deps\n\n // Persist to localStorage on change\n useEffect(() => {\n localStorage.setItem(STORAGE_KEY, activeTabId)\n }, [activeTabId])\n\n const activeTab = tabs.find((tab) => tab.id === activeTabId)\n\n // Build groups for the active tab\n const filteredGroups = useMemo(() => {\n if (!activeTab) {\n return groups\n }\n\n const { collections: tabCollections, customItems, globals: tabGlobals } = activeTab\n\n // If no specific collections/globals defined, show all\n const showAll = !tabCollections && !tabGlobals\n const allowedSlugs = new Set([...(tabCollections ?? []), ...(tabGlobals ?? [])])\n\n let result: ExtendedGroup[] = []\n\n if (showAll) {\n result = groups.map((g) => ({ ...g, entities: [...g.entities] }))\n } else if (allowedSlugs.size > 0) {\n result = groups\n .map((group) => ({\n ...group,\n entities: group.entities.filter((entity) => allowedSlugs.has(entity.slug)),\n }))\n .filter((group) => group.entities.length > 0)\n }\n\n // Merge custom items into groups\n if (customItems && customItems.length > 0) {\n const ungroupedItems: SidebarTabItem[] = []\n\n for (const item of customItems) {\n if (item.group) {\n // Get localized group name for comparison\n const itemGroupLabel = extractLocalizedValue(item.group, currentLang)\n\n // Find existing group by comparing localized labels\n const existingGroup = result.find((g) => {\n const groupLabel = extractLocalizedValue(g.label, currentLang)\n return groupLabel === itemGroupLabel\n })\n\n if (existingGroup) {\n existingGroup.entities.push({\n slug: item.slug,\n type: 'custom',\n href: item.href,\n label: item.label,\n })\n } else {\n // Create new group\n result.push({\n entities: [\n {\n slug: item.slug,\n type: 'custom',\n href: item.href,\n label: item.label,\n },\n ],\n label: item.group,\n })\n }\n } else {\n ungroupedItems.push(item)\n }\n }\n\n // Add ungrouped items at the end\n if (ungroupedItems.length > 0) {\n result.push({\n entities: ungroupedItems.map((item) => ({\n slug: item.slug,\n type: 'custom',\n href: item.href,\n label: item.label,\n })),\n label: '',\n })\n }\n }\n\n return result\n }, [activeTab, groups, currentLang])\n\n return (\n <SidebarWrapper baseClass={baseClass}>\n <TabsBar\n activeTabId={activeTabId}\n onTabChange={setActiveTabId}\n sidebarConfig={sidebarConfig}\n />\n <nav className={`${baseClass}__content`}>\n <div className={`${baseClass}__content-scroll`}>\n {beforeNavLinks}\n <EnhancedSidebarClient groups={filteredGroups} navPreferences={navPreferences} />\n {afterNavLinks}\n </div>\n </nav>\n </SidebarWrapper>\n )\n}\n"],"names":["useTranslation","React","useEffect","useMemo","useState","extractLocalizedValue","EnhancedSidebarClient","SidebarWrapper","TabsBar","baseClass","STORAGE_KEY","SidebarContent","afterNavLinks","beforeNavLinks","groups","navPreferences","sidebarConfig","i18n","currentLang","language","tabs","filter","t","type","defaultTabId","id","activeTabId","setActiveTabId","stored","localStorage","getItem","some","setItem","activeTab","find","tab","filteredGroups","collections","tabCollections","customItems","globals","tabGlobals","showAll","allowedSlugs","Set","result","map","g","entities","size","group","entity","has","slug","length","ungroupedItems","item","itemGroupLabel","existingGroup","groupLabel","label","push","href","onTabChange","nav","className","div"],"mappings":"AAAA;;AAGA,SAASA,cAAc,QAAQ,iBAAgB;AAC/C,OAAOC,SAASC,SAAS,EAAEC,OAAO,EAAEC,QAAQ,QAAQ,QAAO;
|
|
1
|
+
{"version":3,"sources":["../../../src/components/EnhancedSidebar/SidebarContent.tsx"],"sourcesContent":["'use client'\nimport type { NavPreferences } from 'payload'\n\nimport { useTranslation } from '@payloadcms/ui'\nimport React, { useEffect, useMemo, useState } from 'react'\n\nimport type {\n EnhancedSidebarConfig,\n ExtendedEntity,\n ExtendedGroup,\n SidebarTabContent as SidebarTabContentType,\n SidebarTabItem,\n} from '../../types'\n\nimport { extractLocalizedValue } from '../../utils'\nimport { EnhancedSidebarClient } from './index.client'\nimport { SidebarWrapper } from './SidebarWrapper'\nimport { TabsBar } from './TabsBar'\n\nconst baseClass = 'enhanced-sidebar'\n\nexport type SidebarContentProps = {\n afterNavLinks?: React.ReactNode\n beforeNavLinks?: React.ReactNode\n groups: ExtendedGroup[]\n navPreferences: NavPreferences | null\n sidebarConfig: EnhancedSidebarConfig\n}\n\nconst STORAGE_KEY = 'payload-enhanced-sidebar-active-tab'\n\nexport const SidebarContent: React.FC<SidebarContentProps> = ({\n afterNavLinks,\n beforeNavLinks,\n groups,\n navPreferences,\n sidebarConfig,\n}) => {\n const { i18n } = useTranslation()\n const currentLang = i18n.language\n\n const tabs = sidebarConfig.tabs?.filter((t): t is SidebarTabContentType => t.type === 'tab') ?? []\n const defaultTabId = tabs[0]?.id ?? 'default'\n\n // Always start with default to match server render\n const [activeTabId, setActiveTabId] = useState(defaultTabId)\n\n // Read from localStorage only after hydration\n useEffect(() => {\n const stored = localStorage.getItem(STORAGE_KEY)\n if (stored && tabs.some((t) => t.id === stored)) {\n setActiveTabId(stored)\n }\n }, []) // eslint-disable-line react-hooks/exhaustive-deps\n\n // Persist to localStorage on change\n useEffect(() => {\n localStorage.setItem(STORAGE_KEY, activeTabId)\n }, [activeTabId])\n\n const activeTab = tabs.find((tab) => tab.id === activeTabId)\n\n // Build groups for the active tab\n const filteredGroups = useMemo(() => {\n if (!activeTab) {\n return groups\n }\n\n const { collections: tabCollections, customItems, globals: tabGlobals } = activeTab\n\n // If no specific collections/globals defined, show all\n const showAll = !tabCollections && !tabGlobals\n const allowedSlugs = new Set([...(tabCollections ?? []), ...(tabGlobals ?? [])])\n\n let result: ExtendedGroup[] = []\n\n if (showAll) {\n result = groups.map((g) => ({ ...g, entities: [...g.entities] }))\n } else if (allowedSlugs.size > 0) {\n result = groups\n .map((group) => ({\n ...group,\n entities: group.entities.filter((entity) => allowedSlugs.has(entity.slug)),\n }))\n .filter((group) => group.entities.length > 0)\n }\n\n // Merge custom items into groups\n if (customItems && customItems.length > 0) {\n const ungroupedItems: SidebarTabItem[] = []\n\n for (const item of customItems) {\n if (item.group) {\n // Get localized group name for comparison\n const itemGroupLabel = extractLocalizedValue(item.group, currentLang)\n\n // Find existing group by comparing localized labels\n const existingGroup = result.find((g) => {\n const groupLabel = extractLocalizedValue(g.label, currentLang)\n return groupLabel === itemGroupLabel\n })\n\n if (existingGroup) {\n existingGroup.entities.push({\n slug: item.slug,\n type: 'custom',\n href: item.href,\n isExternal: item.isExternal,\n label: item.label,\n } as ExtendedEntity)\n } else {\n // Create new group\n result.push({\n entities: [\n {\n slug: item.slug,\n type: 'custom',\n href: item.href,\n isExternal: item.isExternal,\n label: item.label,\n } as ExtendedEntity,\n ],\n label: item.group,\n })\n }\n } else {\n ungroupedItems.push(item)\n }\n }\n\n // Add ungrouped items at the end\n if (ungroupedItems.length > 0) {\n result.push({\n entities: ungroupedItems.map((item) => ({\n slug: item.slug,\n type: 'custom',\n href: item.href,\n isExternal: item.isExternal,\n label: item.label,\n })) as ExtendedEntity[],\n label: '',\n })\n }\n }\n\n return result\n }, [activeTab, groups, currentLang])\n\n return (\n <SidebarWrapper baseClass={baseClass}>\n <TabsBar\n activeTabId={activeTabId}\n onTabChange={setActiveTabId}\n sidebarConfig={sidebarConfig}\n />\n <nav className={`${baseClass}__content`}>\n <div className={`${baseClass}__content-scroll`}>\n {beforeNavLinks}\n <EnhancedSidebarClient groups={filteredGroups} navPreferences={navPreferences} />\n {afterNavLinks}\n </div>\n </nav>\n </SidebarWrapper>\n )\n}\n"],"names":["useTranslation","React","useEffect","useMemo","useState","extractLocalizedValue","EnhancedSidebarClient","SidebarWrapper","TabsBar","baseClass","STORAGE_KEY","SidebarContent","afterNavLinks","beforeNavLinks","groups","navPreferences","sidebarConfig","i18n","currentLang","language","tabs","filter","t","type","defaultTabId","id","activeTabId","setActiveTabId","stored","localStorage","getItem","some","setItem","activeTab","find","tab","filteredGroups","collections","tabCollections","customItems","globals","tabGlobals","showAll","allowedSlugs","Set","result","map","g","entities","size","group","entity","has","slug","length","ungroupedItems","item","itemGroupLabel","existingGroup","groupLabel","label","push","href","isExternal","onTabChange","nav","className","div"],"mappings":"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"}
|
|
@@ -29,7 +29,7 @@ export const TabsBar = ({ activeTabId, onTabChange, sidebarConfig })=>{
|
|
|
29
29
|
};
|
|
30
30
|
const renderLink = (link)=>{
|
|
31
31
|
const label = getTranslation(link.label, i18n);
|
|
32
|
-
const href = formatAdminURL({
|
|
32
|
+
const href = link.isExternal ? link.href : formatAdminURL({
|
|
33
33
|
adminRoute,
|
|
34
34
|
path: link.href
|
|
35
35
|
});
|
|
@@ -38,6 +38,7 @@ export const TabsBar = ({ activeTabId, onTabChange, sidebarConfig })=>{
|
|
|
38
38
|
return /*#__PURE__*/ _jsx(Link, {
|
|
39
39
|
className: `${tabsBaseClass}__link ${isActive ? `${tabsBaseClass}__link--active` : ''}`,
|
|
40
40
|
href: href,
|
|
41
|
+
target: link.isExternal ? '_blank' : undefined,
|
|
41
42
|
title: label,
|
|
42
43
|
children: /*#__PURE__*/ _jsx(Icon, {
|
|
43
44
|
name: link.icon,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/components/EnhancedSidebar/TabsBar/index.tsx"],"sourcesContent":["'use client'\n\nimport { getTranslation } from '@payloadcms/translations'\nimport { Link, useConfig, useTranslation } from '@payloadcms/ui'\nimport { usePathname } from 'next/navigation'\nimport { formatAdminURL } from 'payload/shared'\nimport React from 'react'\n\nimport type { EnhancedSidebarConfig, SidebarTabContent, SidebarTabLink } from '../../../types'\n\nimport { Icon } from '../Icon'\nimport './index.scss'\n\nconst tabsBaseClass = 'tabs-bar'\n\nexport type TabsBarProps = {\n activeTabId: string\n onTabChange: (tabId: string) => void\n sidebarConfig: EnhancedSidebarConfig\n}\n\nexport const TabsBar: React.FC<TabsBarProps> = ({ activeTabId, onTabChange, sidebarConfig }) => {\n const { i18n } = useTranslation()\n const pathname = usePathname()\n\n const {\n config: {\n admin: {\n routes: { logout: logoutRoute },\n },\n routes: { admin: adminRoute },\n },\n } = useConfig()\n\n const showLogout = sidebarConfig.showLogout !== false\n\n const renderTab = (tab: SidebarTabContent) => {\n const label = getTranslation(tab.label, i18n)\n const isActive = activeTabId === tab.id\n\n return (\n <button\n className={`${tabsBaseClass}__tab ${isActive ? `${tabsBaseClass}__tab--active` : ''}`}\n key={tab.id}\n onClick={() => onTabChange(tab.id)}\n title={label}\n type=\"button\"\n >\n <Icon name={tab.icon} size={20} />\n </button>\n )\n }\n\n const renderLink = (link: SidebarTabLink) => {\n const label = getTranslation(link.label, i18n)\n const href = formatAdminURL({ adminRoute, path: link.href
|
|
1
|
+
{"version":3,"sources":["../../../../src/components/EnhancedSidebar/TabsBar/index.tsx"],"sourcesContent":["'use client'\n\nimport { getTranslation } from '@payloadcms/translations'\nimport { Link, useConfig, useTranslation } from '@payloadcms/ui'\nimport { usePathname } from 'next/navigation'\nimport { formatAdminURL } from 'payload/shared'\nimport React from 'react'\n\nimport type { EnhancedSidebarConfig, SidebarTabContent, SidebarTabLink } from '../../../types'\n\nimport { Icon } from '../Icon'\nimport './index.scss'\n\nconst tabsBaseClass = 'tabs-bar'\n\nexport type TabsBarProps = {\n activeTabId: string\n onTabChange: (tabId: string) => void\n sidebarConfig: EnhancedSidebarConfig\n}\n\nexport const TabsBar: React.FC<TabsBarProps> = ({ activeTabId, onTabChange, sidebarConfig }) => {\n const { i18n } = useTranslation()\n const pathname = usePathname()\n\n const {\n config: {\n admin: {\n routes: { logout: logoutRoute },\n },\n routes: { admin: adminRoute },\n },\n } = useConfig()\n\n const showLogout = sidebarConfig.showLogout !== false\n\n const renderTab = (tab: SidebarTabContent) => {\n const label = getTranslation(tab.label, i18n)\n const isActive = activeTabId === tab.id\n\n return (\n <button\n className={`${tabsBaseClass}__tab ${isActive ? `${tabsBaseClass}__tab--active` : ''}`}\n key={tab.id}\n onClick={() => onTabChange(tab.id)}\n title={label}\n type=\"button\"\n >\n <Icon name={tab.icon} size={20} />\n </button>\n )\n }\n\n const renderLink = (link: SidebarTabLink) => {\n const label = getTranslation(link.label, i18n)\n const href = link.isExternal ? link.href : formatAdminURL({ adminRoute, path: link.href })\n\n // Check if this link is active\n const isActive = pathname === href || (link.href === '/' && pathname === adminRoute)\n\n return (\n <Link\n className={`${tabsBaseClass}__link ${isActive ? `${tabsBaseClass}__link--active` : ''}`}\n href={href}\n key={link.id}\n target={link.isExternal ? '_blank' : undefined}\n title={label}\n >\n <Icon name={link.icon} size={20} />\n </Link>\n )\n }\n\n const renderTabItem = (item: SidebarTabContent | SidebarTabLink) => {\n if (item.type === 'tab') {\n return renderTab(item)\n }\n return renderLink(item)\n }\n\n const tabItems = sidebarConfig.tabs ?? []\n\n return (\n <div className={tabsBaseClass}>\n <div className={`${tabsBaseClass}__tabs`}>{tabItems.map(renderTabItem)}</div>\n\n {showLogout && (\n <div className={`${tabsBaseClass}__actions`}>\n <Link\n className={`${tabsBaseClass}__action`}\n href={formatAdminURL({\n adminRoute,\n path: logoutRoute,\n })}\n title={getTranslation({ en: 'Logout', uk: 'Вийти' }, i18n)}\n type=\"button\"\n >\n <Icon name=\"LogOut\" size={20} />\n </Link>\n </div>\n )}\n </div>\n )\n}\n"],"names":["getTranslation","Link","useConfig","useTranslation","usePathname","formatAdminURL","React","Icon","tabsBaseClass","TabsBar","activeTabId","onTabChange","sidebarConfig","i18n","pathname","config","admin","routes","logout","logoutRoute","adminRoute","showLogout","renderTab","tab","label","isActive","id","button","className","onClick","title","type","name","icon","size","renderLink","link","href","isExternal","path","target","undefined","renderTabItem","item","tabItems","tabs","div","map","en","uk"],"mappings":"AAAA;;AAEA,SAASA,cAAc,QAAQ,2BAA0B;AACzD,SAASC,IAAI,EAAEC,SAAS,EAAEC,cAAc,QAAQ,iBAAgB;AAChE,SAASC,WAAW,QAAQ,kBAAiB;AAC7C,SAASC,cAAc,QAAQ,iBAAgB;AAC/C,OAAOC,WAAW,QAAO;AAIzB,SAASC,IAAI,QAAQ,UAAS;AAC9B,OAAO,eAAc;AAErB,MAAMC,gBAAgB;AAQtB,OAAO,MAAMC,UAAkC,CAAC,EAAEC,WAAW,EAAEC,WAAW,EAAEC,aAAa,EAAE;IACzF,MAAM,EAAEC,IAAI,EAAE,GAAGV;IACjB,MAAMW,WAAWV;IAEjB,MAAM,EACJW,QAAQ,EACNC,OAAO,EACLC,QAAQ,EAAEC,QAAQC,WAAW,EAAE,EAChC,EACDF,QAAQ,EAAED,OAAOI,UAAU,EAAE,EAC9B,EACF,GAAGlB;IAEJ,MAAMmB,aAAaT,cAAcS,UAAU,KAAK;IAEhD,MAAMC,YAAY,CAACC;QACjB,MAAMC,QAAQxB,eAAeuB,IAAIC,KAAK,EAAEX;QACxC,MAAMY,WAAWf,gBAAgBa,IAAIG,EAAE;QAEvC,qBACE,KAACC;YACCC,WAAW,GAAGpB,cAAc,MAAM,EAAEiB,WAAW,GAAGjB,cAAc,aAAa,CAAC,GAAG,IAAI;YAErFqB,SAAS,IAAMlB,YAAYY,IAAIG,EAAE;YACjCI,OAAON;YACPO,MAAK;sBAEL,cAAA,KAACxB;gBAAKyB,MAAMT,IAAIU,IAAI;gBAAEC,MAAM;;WALvBX,IAAIG,EAAE;IAQjB;IAEA,MAAMS,aAAa,CAACC;QAClB,MAAMZ,QAAQxB,eAAeoC,KAAKZ,KAAK,EAAEX;QACzC,MAAMwB,OAAOD,KAAKE,UAAU,GAAGF,KAAKC,IAAI,GAAGhC,eAAe;YAAEe;YAAYmB,MAAMH,KAAKC,IAAI;QAAC;QAExF,+BAA+B;QAC/B,MAAMZ,WAAWX,aAAauB,QAASD,KAAKC,IAAI,KAAK,OAAOvB,aAAaM;QAEzE,qBACE,KAACnB;YACC2B,WAAW,GAAGpB,cAAc,OAAO,EAAEiB,WAAW,GAAGjB,cAAc,cAAc,CAAC,GAAG,IAAI;YACvF6B,MAAMA;YAENG,QAAQJ,KAAKE,UAAU,GAAG,WAAWG;YACrCX,OAAON;sBAEP,cAAA,KAACjB;gBAAKyB,MAAMI,KAAKH,IAAI;gBAAEC,MAAM;;WAJxBE,KAAKV,EAAE;IAOlB;IAEA,MAAMgB,gBAAgB,CAACC;QACrB,IAAIA,KAAKZ,IAAI,KAAK,OAAO;YACvB,OAAOT,UAAUqB;QACnB;QACA,OAAOR,WAAWQ;IACpB;IAEA,MAAMC,WAAWhC,cAAciC,IAAI,IAAI,EAAE;IAEzC,qBACE,MAACC;QAAIlB,WAAWpB;;0BACd,KAACsC;gBAAIlB,WAAW,GAAGpB,cAAc,MAAM,CAAC;0BAAGoC,SAASG,GAAG,CAACL;;YAEvDrB,4BACC,KAACyB;gBAAIlB,WAAW,GAAGpB,cAAc,SAAS,CAAC;0BACzC,cAAA,KAACP;oBACC2B,WAAW,GAAGpB,cAAc,QAAQ,CAAC;oBACrC6B,MAAMhC,eAAe;wBACnBe;wBACAmB,MAAMpB;oBACR;oBACAW,OAAO9B,eAAe;wBAAEgD,IAAI;wBAAUC,IAAI;oBAAQ,GAAGpC;oBACrDkB,MAAK;8BAEL,cAAA,KAACxB;wBAAKyB,MAAK;wBAASE,MAAM;;;;;;AAMtC,EAAC"}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
3
|
import { getTranslation } from '@payloadcms/translations';
|
|
4
4
|
import { Link, NavGroup, useConfig, useTranslation } from '@payloadcms/ui';
|
|
5
|
+
import { EntityType } from '@payloadcms/ui/shared';
|
|
5
6
|
import { usePathname } from 'next/navigation.js';
|
|
6
7
|
import { formatAdminURL } from 'payload/shared';
|
|
7
8
|
import React, { Fragment } from 'react';
|
|
@@ -20,14 +21,16 @@ export const EnhancedSidebarClient = ({ groups, navPreferences })=>{
|
|
|
20
21
|
const entityType = entity.type;
|
|
21
22
|
let href;
|
|
22
23
|
let id;
|
|
23
|
-
// Check for collection
|
|
24
|
-
|
|
24
|
+
// Check for collection
|
|
25
|
+
//@ts-expect-error Idk why typescript is complaining here
|
|
26
|
+
if (entityType === EntityType.collection) {
|
|
25
27
|
href = formatAdminURL({
|
|
26
28
|
adminRoute,
|
|
27
29
|
path: `/collections/${slug}`
|
|
28
30
|
});
|
|
29
31
|
id = `nav-${slug}`;
|
|
30
|
-
|
|
32
|
+
//@ts-expect-error Idk why typescript is complaining here
|
|
33
|
+
} else if (entityType === EntityType.global) {
|
|
31
34
|
href = formatAdminURL({
|
|
32
35
|
adminRoute,
|
|
33
36
|
path: `/globals/${slug}`
|
|
@@ -35,12 +38,15 @@ export const EnhancedSidebarClient = ({ groups, navPreferences })=>{
|
|
|
35
38
|
id = `nav-global-${slug}`;
|
|
36
39
|
} else if (entityType === 'custom' && entity.href) {
|
|
37
40
|
// Custom item with href
|
|
38
|
-
const customHref = entity.href;
|
|
39
|
-
href = formatAdminURL({
|
|
40
|
-
adminRoute,
|
|
41
|
-
path: customHref
|
|
42
|
-
});
|
|
43
41
|
id = `nav-custom-${slug}`;
|
|
42
|
+
if (entity.isExternal) {
|
|
43
|
+
href = entity.href;
|
|
44
|
+
} else {
|
|
45
|
+
href = formatAdminURL({
|
|
46
|
+
adminRoute,
|
|
47
|
+
path: entity.href
|
|
48
|
+
});
|
|
49
|
+
}
|
|
44
50
|
} else {
|
|
45
51
|
return null;
|
|
46
52
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/EnhancedSidebar/index.client.tsx"],"sourcesContent":["'use client'\nimport type { NavPreferences } from 'payload'\nimport type { ExtendedGroup } from 'src/types'\n\nimport { getTranslation } from '@payloadcms/translations'\nimport { Link, NavGroup, useConfig, useTranslation } from '@payloadcms/ui'\nimport { usePathname } from 'next/navigation.js'\nimport { formatAdminURL } from 'payload/shared'\nimport React, { Fragment } from 'react'\n\nconst baseClass = 'enhanced-sidebar'\n\nexport const EnhancedSidebarClient: React.FC<{\n groups: ExtendedGroup[]\n navPreferences: NavPreferences | null\n}> = ({ groups, navPreferences }) => {\n const pathname = usePathname()\n\n const {\n config: {\n routes: { admin: adminRoute },\n },\n } = useConfig()\n\n const { i18n } = useTranslation()\n\n return (\n <Fragment>\n {groups.map(({ entities, label }, key) => {\n // Handle empty label (ungrouped items)\n const groupLabel = label || ''\n const isUngrouped = !label || (typeof label === 'string' && label === '')\n\n const content = entities.map((entity, i) => {\n const { slug, label: entityLabel } = entity\n const entityType = entity.type\n let href: string\n let id: string\n\n // Check for collection
|
|
1
|
+
{"version":3,"sources":["../../../src/components/EnhancedSidebar/index.client.tsx"],"sourcesContent":["'use client'\nimport type { NavPreferences } from 'payload'\nimport type { ExtendedGroup } from 'src/types'\n\nimport { getTranslation } from '@payloadcms/translations'\nimport { Link, NavGroup, useConfig, useTranslation } from '@payloadcms/ui'\nimport { EntityType } from '@payloadcms/ui/shared'\nimport { usePathname } from 'next/navigation.js'\nimport { formatAdminURL } from 'payload/shared'\nimport React, { Fragment } from 'react'\n\nconst baseClass = 'enhanced-sidebar'\n\nexport const EnhancedSidebarClient: React.FC<{\n groups: ExtendedGroup[]\n navPreferences: NavPreferences | null\n}> = ({ groups, navPreferences }) => {\n const pathname = usePathname()\n\n const {\n config: {\n routes: { admin: adminRoute },\n },\n } = useConfig()\n\n const { i18n } = useTranslation()\n\n return (\n <Fragment>\n {groups.map(({ entities, label }, key) => {\n // Handle empty label (ungrouped items)\n const groupLabel = label || ''\n const isUngrouped = !label || (typeof label === 'string' && label === '')\n\n const content = entities.map((entity, i) => {\n const { slug, label: entityLabel } = entity\n const entityType = entity.type\n let href: string\n let id: string\n\n // Check for collection\n //@ts-expect-error Idk why typescript is complaining here\n if (entityType === EntityType.collection) {\n href = formatAdminURL({ adminRoute, path: `/collections/${slug}` })\n id = `nav-${slug}`\n //@ts-expect-error Idk why typescript is complaining here\n } else if (entityType === EntityType.global) {\n href = formatAdminURL({ adminRoute, path: `/globals/${slug}` })\n id = `nav-global-${slug}`\n } else if (entityType === 'custom' && entity.href) {\n // Custom item with href\n id = `nav-custom-${slug}`\n if (entity.isExternal) {\n href = entity.href\n } else {\n href = formatAdminURL({ adminRoute, path: entity.href })\n }\n } else {\n return null\n }\n\n const isActive =\n pathname.startsWith(href) && ['/', undefined].includes(pathname[href.length])\n\n const Label = (\n <>\n {isActive && <div className={`${baseClass}__link-indicator`} />}\n <span className={`${baseClass}__link-label`}>\n {getTranslation(entityLabel, i18n)}\n </span>\n </>\n )\n\n if (pathname === href) {\n return (\n <div className={`${baseClass}__link`} id={id} key={i}>\n {Label}\n </div>\n )\n }\n\n return (\n <Link className={`${baseClass}__link`} href={href} id={id} key={i} prefetch={false}>\n {Label}\n </Link>\n )\n })\n\n // For ungrouped items, render without NavGroup wrapper\n if (isUngrouped) {\n return <Fragment key={key}>{content}</Fragment>\n }\n\n // Get translated label for NavGroup\n const translatedLabel = getTranslation(groupLabel, i18n)\n\n return (\n <NavGroup\n isOpen={navPreferences?.groups?.[translatedLabel]?.open}\n key={key}\n label={translatedLabel}\n >\n {content}\n </NavGroup>\n )\n })}\n </Fragment>\n )\n}\n"],"names":["getTranslation","Link","NavGroup","useConfig","useTranslation","EntityType","usePathname","formatAdminURL","React","Fragment","baseClass","EnhancedSidebarClient","groups","navPreferences","pathname","config","routes","admin","adminRoute","i18n","map","entities","label","key","groupLabel","isUngrouped","content","entity","i","slug","entityLabel","entityType","type","href","id","collection","path","global","isExternal","isActive","startsWith","undefined","includes","length","Label","div","className","span","prefetch","translatedLabel","isOpen","open"],"mappings":"AAAA;;AAIA,SAASA,cAAc,QAAQ,2BAA0B;AACzD,SAASC,IAAI,EAAEC,QAAQ,EAAEC,SAAS,EAAEC,cAAc,QAAQ,iBAAgB;AAC1E,SAASC,UAAU,QAAQ,wBAAuB;AAClD,SAASC,WAAW,QAAQ,qBAAoB;AAChD,SAASC,cAAc,QAAQ,iBAAgB;AAC/C,OAAOC,SAASC,QAAQ,QAAQ,QAAO;AAEvC,MAAMC,YAAY;AAElB,OAAO,MAAMC,wBAGR,CAAC,EAAEC,MAAM,EAAEC,cAAc,EAAE;IAC9B,MAAMC,WAAWR;IAEjB,MAAM,EACJS,QAAQ,EACNC,QAAQ,EAAEC,OAAOC,UAAU,EAAE,EAC9B,EACF,GAAGf;IAEJ,MAAM,EAAEgB,IAAI,EAAE,GAAGf;IAEjB,qBACE,KAACK;kBACEG,OAAOQ,GAAG,CAAC,CAAC,EAAEC,QAAQ,EAAEC,KAAK,EAAE,EAAEC;YAChC,uCAAuC;YACvC,MAAMC,aAAaF,SAAS;YAC5B,MAAMG,cAAc,CAACH,SAAU,OAAOA,UAAU,YAAYA,UAAU;YAEtE,MAAMI,UAAUL,SAASD,GAAG,CAAC,CAACO,QAAQC;gBACpC,MAAM,EAAEC,IAAI,EAAEP,OAAOQ,WAAW,EAAE,GAAGH;gBACrC,MAAMI,aAAaJ,OAAOK,IAAI;gBAC9B,IAAIC;gBACJ,IAAIC;gBAEJ,uBAAuB;gBACvB,yDAAyD;gBACzD,IAAIH,eAAe1B,WAAW8B,UAAU,EAAE;oBACxCF,OAAO1B,eAAe;wBAAEW;wBAAYkB,MAAM,CAAC,aAAa,EAAEP,MAAM;oBAAC;oBACjEK,KAAK,CAAC,IAAI,EAAEL,MAAM;gBAClB,yDAAyD;gBAC3D,OAAO,IAAIE,eAAe1B,WAAWgC,MAAM,EAAE;oBAC3CJ,OAAO1B,eAAe;wBAAEW;wBAAYkB,MAAM,CAAC,SAAS,EAAEP,MAAM;oBAAC;oBAC7DK,KAAK,CAAC,WAAW,EAAEL,MAAM;gBAC3B,OAAO,IAAIE,eAAe,YAAYJ,OAAOM,IAAI,EAAE;oBACjD,wBAAwB;oBACxBC,KAAK,CAAC,WAAW,EAAEL,MAAM;oBACzB,IAAIF,OAAOW,UAAU,EAAE;wBACrBL,OAAON,OAAOM,IAAI;oBACpB,OAAO;wBACLA,OAAO1B,eAAe;4BAAEW;4BAAYkB,MAAMT,OAAOM,IAAI;wBAAC;oBACxD;gBACF,OAAO;oBACL,OAAO;gBACT;gBAEA,MAAMM,WACJzB,SAAS0B,UAAU,CAACP,SAAS;oBAAC;oBAAKQ;iBAAU,CAACC,QAAQ,CAAC5B,QAAQ,CAACmB,KAAKU,MAAM,CAAC;gBAE9E,MAAMC,sBACJ;;wBACGL,0BAAY,KAACM;4BAAIC,WAAW,GAAGpC,UAAU,gBAAgB,CAAC;;sCAC3D,KAACqC;4BAAKD,WAAW,GAAGpC,UAAU,YAAY,CAAC;sCACxCV,eAAe8B,aAAaX;;;;gBAKnC,IAAIL,aAAamB,MAAM;oBACrB,qBACE,KAACY;wBAAIC,WAAW,GAAGpC,UAAU,MAAM,CAAC;wBAAEwB,IAAIA;kCACvCU;uBADgDhB;gBAIvD;gBAEA,qBACE,KAAC3B;oBAAK6C,WAAW,GAAGpC,UAAU,MAAM,CAAC;oBAAEuB,MAAMA;oBAAMC,IAAIA;oBAAYc,UAAU;8BAC1EJ;mBAD6DhB;YAIpE;YAEA,uDAAuD;YACvD,IAAIH,aAAa;gBACf,qBAAO,KAAChB;8BAAoBiB;mBAANH;YACxB;YAEA,oCAAoC;YACpC,MAAM0B,kBAAkBjD,eAAewB,YAAYL;YAEnD,qBACE,KAACjB;gBACCgD,QAAQrC,gBAAgBD,QAAQ,CAACqC,gBAAgB,EAAEE;gBAEnD7B,OAAO2B;0BAENvB;eAHIH;QAMX;;AAGN,EAAC"}
|
package/dist/types.d.ts
CHANGED
|
@@ -35,12 +35,7 @@ export interface SidebarTabContent {
|
|
|
35
35
|
label: LocalizedString;
|
|
36
36
|
type: 'tab';
|
|
37
37
|
}
|
|
38
|
-
|
|
39
|
-
* Sidebar link that navigates to a URL (not a tab)
|
|
40
|
-
*/
|
|
41
|
-
export interface SidebarTabLink {
|
|
42
|
-
/** Link href (relative to admin route) */
|
|
43
|
-
href: string;
|
|
38
|
+
interface SidebarTabLinkBase {
|
|
44
39
|
/** Icon name from lucide-react */
|
|
45
40
|
icon: IconName;
|
|
46
41
|
/** Unique identifier */
|
|
@@ -49,6 +44,20 @@ export interface SidebarTabLink {
|
|
|
49
44
|
label: LocalizedString;
|
|
50
45
|
type: 'link';
|
|
51
46
|
}
|
|
47
|
+
interface SidebarTabLinkExternal extends SidebarTabLinkBase {
|
|
48
|
+
/** Link href (absolute URL) */
|
|
49
|
+
href: string;
|
|
50
|
+
isExternal: true;
|
|
51
|
+
}
|
|
52
|
+
interface SidebarTabLinkInternal extends SidebarTabLinkBase {
|
|
53
|
+
/** Link href (relative to admin route) */
|
|
54
|
+
href: '' | `/${string}`;
|
|
55
|
+
isExternal?: false;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Sidebar link that navigates to a URL (not a tab)
|
|
59
|
+
*/
|
|
60
|
+
export type SidebarTabLink = SidebarTabLinkExternal | SidebarTabLinkInternal;
|
|
52
61
|
/**
|
|
53
62
|
* A tab or link in the sidebar tabs bar
|
|
54
63
|
*/
|
|
@@ -119,12 +128,20 @@ export type GenericCollectionDocument = {
|
|
|
119
128
|
[key: string]: number | string;
|
|
120
129
|
id: string;
|
|
121
130
|
};
|
|
122
|
-
|
|
123
|
-
href?: string;
|
|
131
|
+
interface BaseExtendedEntity {
|
|
124
132
|
label: Record<string, string> | string;
|
|
125
133
|
slug: string;
|
|
126
134
|
type: 'collection' | 'custom' | 'global';
|
|
127
|
-
}
|
|
135
|
+
}
|
|
136
|
+
interface InternalExtendedEntity extends BaseExtendedEntity {
|
|
137
|
+
href?: '' | `/${string}`;
|
|
138
|
+
isExternal?: false;
|
|
139
|
+
}
|
|
140
|
+
interface ExternalExtendedEntity extends BaseExtendedEntity {
|
|
141
|
+
href: string;
|
|
142
|
+
isExternal: true;
|
|
143
|
+
}
|
|
144
|
+
export type ExtendedEntity = ExternalExtendedEntity | InternalExtendedEntity;
|
|
128
145
|
export type ExtendedGroup = {
|
|
129
146
|
entities: ExtendedEntity[];
|
|
130
147
|
label: Record<string, string> | string;
|
package/dist/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/types.ts"],"sourcesContent":["import type { icons, LucideIcon } from 'lucide-react'\nimport type { CollectionSlug, GlobalSlug } from 'payload'\n\nexport type IconName = keyof typeof icons\n\nexport type LocalizedString = { [locale: string]: string } | string\n\nexport type InternalIcon = IconName | LucideIcon\n\n// ============================================\n// Enhanced Sidebar Types\n// ============================================\n\n/**\n * Sidebar tab that shows content when selected\n */\nexport interface SidebarTabContent {\n /**\n * Collections to show in this tab.\n * If not specified, no collections are shown (unless items are specified).\n * Use collection slugs.\n */\n collections?: CollectionSlug[]\n /**\n * Custom items to add to this tab.\n * Items with `group` will be merged into matching collection groups.\n * Items without `group` will be shown at the bottom as a flat list.\n */\n customItems?: SidebarTabItem[]\n /**\n * Globals to show in this tab.\n * If not specified, no globals are shown.\n * Use global slugs.\n */\n globals?: GlobalSlug[]\n /** Icon name from lucide-react */\n icon: IconName\n /** Unique identifier for the tab */\n id: string\n /** Tooltip/label for the tab */\n label: LocalizedString\n type: 'tab'\n}\n\
|
|
1
|
+
{"version":3,"sources":["../src/types.ts"],"sourcesContent":["import type { icons, LucideIcon } from 'lucide-react'\nimport type { CollectionSlug, GlobalSlug } from 'payload'\n\nexport type IconName = keyof typeof icons\n\nexport type LocalizedString = { [locale: string]: string } | string\n\nexport type InternalIcon = IconName | LucideIcon\n\n// ============================================\n// Enhanced Sidebar Types\n// ============================================\n\n/**\n * Sidebar tab that shows content when selected\n */\nexport interface SidebarTabContent {\n /**\n * Collections to show in this tab.\n * If not specified, no collections are shown (unless items are specified).\n * Use collection slugs.\n */\n collections?: CollectionSlug[]\n /**\n * Custom items to add to this tab.\n * Items with `group` will be merged into matching collection groups.\n * Items without `group` will be shown at the bottom as a flat list.\n */\n customItems?: SidebarTabItem[]\n /**\n * Globals to show in this tab.\n * If not specified, no globals are shown.\n * Use global slugs.\n */\n globals?: GlobalSlug[]\n /** Icon name from lucide-react */\n icon: IconName\n /** Unique identifier for the tab */\n id: string\n /** Tooltip/label for the tab */\n label: LocalizedString\n type: 'tab'\n}\n\ninterface SidebarTabLinkBase {\n /** Icon name from lucide-react */\n icon: IconName\n /** Unique identifier */\n id: string\n /** Tooltip/label */\n label: LocalizedString\n type: 'link'\n}\ninterface SidebarTabLinkExternal extends SidebarTabLinkBase {\n /** Link href (absolute URL) */\n href: string\n isExternal: true\n}\ninterface SidebarTabLinkInternal extends SidebarTabLinkBase {\n /** Link href (relative to admin route) */\n href: '' | `/${string}`\n isExternal?: false\n}\n/**\n * Sidebar link that navigates to a URL (not a tab)\n */\nexport type SidebarTabLink = SidebarTabLinkExternal | SidebarTabLinkInternal\n/**\n * A tab or link in the sidebar tabs bar\n */\nexport type SidebarTab = SidebarTabContent | SidebarTabLink\n\ninterface BaseSidebarTabItem {\n /**\n * Group to add this item to.\n * If matches an existing collection group label, item will be merged into that group.\n * If no match found, a new group will be created.\n * If not specified, item will be shown at the bottom as ungrouped.\n */\n group?: LocalizedString\n /** Display label */\n label: LocalizedString\n /** Unique slug for the item */\n slug: string\n}\ninterface ExternalHrefItem extends BaseSidebarTabItem {\n /** Link href (absolute URL or relative to root) */\n href: string\n /** Whether the link is external, without admin route prefix. */\n isExternal: true\n}\n\ninterface InternalHrefItem extends BaseSidebarTabItem {\n /** Link href (relative to admin route) */\n href: '' | `/${string}`\n /** Whether the link is external, without admin route prefix. */\n isExternal?: false\n}\n/**\n * Custom item inside a sidebar tab\n */\nexport type SidebarTabItem = ExternalHrefItem | InternalHrefItem\n\n/**\n * Configuration for the enhanced sidebar\n */\nexport interface EnhancedSidebarConfig {\n /**\n * Disable the plugin\n * @default false\n */\n disabled?: boolean\n\n /**\n * Custom icons for collections and globals in the default tab.\n */\n icons?: {\n collections?: Partial<Record<CollectionSlug, IconName>>\n globals?: Partial<Record<GlobalSlug, IconName>>\n }\n\n /**\n * Show logout button at the bottom of the tabs bar.\n * @default true\n */\n showLogout?: boolean\n\n /**\n * Tabs and links to show in the sidebar tabs bar.\n * Order matters - items are rendered top to bottom.\n *\n * @default [{ type: 'tab', id: 'default', icon: 'LayoutGrid', label: 'Collections' }]\n */\n tabs?: SidebarTab[]\n}\n\n/**\n * Generic document type for collections, with dynamic keys.\n * We assume values are either string or number for simplicity, useAsTitle is making sure of that.\n */\nexport type GenericCollectionDocument = {\n [key: string]: number | string\n id: string\n}\n\ninterface BaseExtendedEntity {\n label: Record<string, string> | string\n slug: string\n type: 'collection' | 'custom' | 'global'\n}\n\ninterface InternalExtendedEntity extends BaseExtendedEntity {\n href?: '' | `/${string}`\n isExternal?: false\n}\n\ninterface ExternalExtendedEntity extends BaseExtendedEntity {\n href: string\n isExternal: true\n}\n\nexport type ExtendedEntity = ExternalExtendedEntity | InternalExtendedEntity\n\nexport type ExtendedGroup = {\n entities: ExtendedEntity[]\n label: Record<string, string> | string\n}\n"],"names":[],"mappings":"AAmKA,WAGC"}
|
package/package.json
CHANGED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
-
import { getTranslation } from '@payloadcms/translations';
|
|
4
|
-
import { Link, useConfig, useTranslation } from '@payloadcms/ui';
|
|
5
|
-
import { usePathname } from 'next/navigation.js';
|
|
6
|
-
import { formatAdminURL } from 'payload/shared';
|
|
7
|
-
import React from 'react';
|
|
8
|
-
const baseClass = 'enhanced-sidebar';
|
|
9
|
-
export const CustomTabContent = ({ items })=>{
|
|
10
|
-
const { i18n } = useTranslation();
|
|
11
|
-
const pathname = usePathname();
|
|
12
|
-
const { config: { routes: { admin: adminRoute } } } = useConfig();
|
|
13
|
-
return /*#__PURE__*/ _jsx("div", {
|
|
14
|
-
className: `${baseClass}__custom-items`,
|
|
15
|
-
children: items.map((item)=>{
|
|
16
|
-
const href = item.isExternal ? item.href : formatAdminURL({
|
|
17
|
-
adminRoute,
|
|
18
|
-
path: item.href
|
|
19
|
-
});
|
|
20
|
-
const label = getTranslation(item.label, i18n);
|
|
21
|
-
const isActive = pathname.startsWith(href);
|
|
22
|
-
return /*#__PURE__*/ _jsx(Link, {
|
|
23
|
-
className: `${baseClass}__link ${isActive ? `${baseClass}__link--active` : ''}`,
|
|
24
|
-
href: href,
|
|
25
|
-
prefetch: false,
|
|
26
|
-
children: /*#__PURE__*/ _jsx("span", {
|
|
27
|
-
className: `${baseClass}__link-label`,
|
|
28
|
-
children: label
|
|
29
|
-
})
|
|
30
|
-
}, item.slug);
|
|
31
|
-
})
|
|
32
|
-
});
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
//# sourceMappingURL=CustomTabContent.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/EnhancedSidebar/CustomTabContent.tsx"],"sourcesContent":["'use client'\n\nimport { getTranslation } from '@payloadcms/translations'\nimport { Link, useConfig, useTranslation } from '@payloadcms/ui'\nimport { usePathname } from 'next/navigation.js'\nimport { formatAdminURL } from 'payload/shared'\nimport React from 'react'\n\nimport type { SidebarTabItem } from '../../types'\n\nconst baseClass = 'enhanced-sidebar'\n\nexport type CustomTabContentProps = {\n items: SidebarTabItem[]\n}\n\nexport const CustomTabContent: React.FC<CustomTabContentProps> = ({ items }) => {\n const { i18n } = useTranslation()\n const pathname = usePathname()\n\n const {\n config: {\n routes: { admin: adminRoute },\n },\n } = useConfig()\n\n return (\n <div className={`${baseClass}__custom-items`}>\n {items.map((item) => {\n const href = item.isExternal ? item.href : formatAdminURL({ adminRoute, path: item.href })\n const label = getTranslation(item.label, i18n)\n const isActive = pathname.startsWith(href)\n\n return (\n <Link\n className={`${baseClass}__link ${isActive ? `${baseClass}__link--active` : ''}`}\n href={href}\n key={item.slug}\n prefetch={false}\n >\n <span className={`${baseClass}__link-label`}>{label}</span>\n </Link>\n )\n })}\n </div>\n )\n}\n"],"names":["getTranslation","Link","useConfig","useTranslation","usePathname","formatAdminURL","React","baseClass","CustomTabContent","items","i18n","pathname","config","routes","admin","adminRoute","div","className","map","item","href","isExternal","path","label","isActive","startsWith","prefetch","span","slug"],"mappings":"AAAA;;AAEA,SAASA,cAAc,QAAQ,2BAA0B;AACzD,SAASC,IAAI,EAAEC,SAAS,EAAEC,cAAc,QAAQ,iBAAgB;AAChE,SAASC,WAAW,QAAQ,qBAAoB;AAChD,SAASC,cAAc,QAAQ,iBAAgB;AAC/C,OAAOC,WAAW,QAAO;AAIzB,MAAMC,YAAY;AAMlB,OAAO,MAAMC,mBAAoD,CAAC,EAAEC,KAAK,EAAE;IACzE,MAAM,EAAEC,IAAI,EAAE,GAAGP;IACjB,MAAMQ,WAAWP;IAEjB,MAAM,EACJQ,QAAQ,EACNC,QAAQ,EAAEC,OAAOC,UAAU,EAAE,EAC9B,EACF,GAAGb;IAEJ,qBACE,KAACc;QAAIC,WAAW,GAAGV,UAAU,cAAc,CAAC;kBACzCE,MAAMS,GAAG,CAAC,CAACC;YACV,MAAMC,OAAOD,KAAKE,UAAU,GAAGF,KAAKC,IAAI,GAAGf,eAAe;gBAAEU;gBAAYO,MAAMH,KAAKC,IAAI;YAAC;YACxF,MAAMG,QAAQvB,eAAemB,KAAKI,KAAK,EAAEb;YACzC,MAAMc,WAAWb,SAASc,UAAU,CAACL;YAErC,qBACE,KAACnB;gBACCgB,WAAW,GAAGV,UAAU,OAAO,EAAEiB,WAAW,GAAGjB,UAAU,cAAc,CAAC,GAAG,IAAI;gBAC/Ea,MAAMA;gBAENM,UAAU;0BAEV,cAAA,KAACC;oBAAKV,WAAW,GAAGV,UAAU,YAAY,CAAC;8BAAGgB;;eAHzCJ,KAAKS,IAAI;QAMpB;;AAGN,EAAC"}
|