@veiag/payload-enhanced-sidebar 0.1.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 +231 -0
- package/dist/components/EnhancedSidebar/CustomTabContent.d.ts +6 -0
- package/dist/components/EnhancedSidebar/CustomTabContent.js +35 -0
- package/dist/components/EnhancedSidebar/CustomTabContent.js.map +1 -0
- package/dist/components/EnhancedSidebar/Icon.d.ts +8 -0
- package/dist/components/EnhancedSidebar/Icon.js +16 -0
- package/dist/components/EnhancedSidebar/Icon.js.map +1 -0
- package/dist/components/EnhancedSidebar/NavHamburger/index.d.ts +4 -0
- package/dist/components/EnhancedSidebar/NavHamburger/index.js +18 -0
- package/dist/components/EnhancedSidebar/NavHamburger/index.js.map +1 -0
- package/dist/components/EnhancedSidebar/SidebarContent.d.ts +11 -0
- package/dist/components/EnhancedSidebar/SidebarContent.js +140 -0
- package/dist/components/EnhancedSidebar/SidebarContent.js.map +1 -0
- package/dist/components/EnhancedSidebar/SidebarWrapper/index.d.ts +6 -0
- package/dist/components/EnhancedSidebar/SidebarWrapper/index.js +33 -0
- package/dist/components/EnhancedSidebar/SidebarWrapper/index.js.map +1 -0
- package/dist/components/EnhancedSidebar/SidebarWrapper/index.scss +63 -0
- package/dist/components/EnhancedSidebar/TabsBar/index.d.ts +9 -0
- package/dist/components/EnhancedSidebar/TabsBar/index.js +85 -0
- package/dist/components/EnhancedSidebar/TabsBar/index.js.map +1 -0
- package/dist/components/EnhancedSidebar/TabsBar/index.scss +108 -0
- package/dist/components/EnhancedSidebar/getNavPrefs.d.ts +2 -0
- package/dist/components/EnhancedSidebar/getNavPrefs.js +31 -0
- package/dist/components/EnhancedSidebar/getNavPrefs.js.map +1 -0
- package/dist/components/EnhancedSidebar/index.client.d.ts +7 -0
- package/dist/components/EnhancedSidebar/index.client.js +94 -0
- package/dist/components/EnhancedSidebar/index.client.js.map +1 -0
- package/dist/components/EnhancedSidebar/index.d.ts +10 -0
- package/dist/components/EnhancedSidebar/index.js +65 -0
- package/dist/components/EnhancedSidebar/index.js.map +1 -0
- package/dist/components/EnhancedSidebar/index.scss +101 -0
- package/dist/exports/client.d.ts +0 -0
- package/dist/exports/client.js +3 -0
- package/dist/exports/client.js.map +1 -0
- package/dist/exports/rsc.d.ts +1 -0
- package/dist/exports/rsc.js +3 -0
- package/dist/exports/rsc.js.map +1 -0
- package/dist/hooks/useMutationObserver.d.ts +1 -0
- package/dist/hooks/useMutationObserver.js +21 -0
- package/dist/hooks/useMutationObserver.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +64 -0
- package/dist/index.js.map +1 -0
- package/dist/translations/index.d.ts +22 -0
- package/dist/translations/index.js +30 -0
- package/dist/translations/index.js.map +1 -0
- package/dist/types.d.ts +132 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/index.d.ts +3 -0
- package/dist/utils/index.js +14 -0
- package/dist/utils/index.js.map +1 -0
- package/package.json +95 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { deepMerge } from 'payload';
|
|
2
|
+
import { sidebarTranslations } from './translations';
|
|
3
|
+
/**
|
|
4
|
+
* Default configuration for the enhanced sidebar
|
|
5
|
+
*/ const defaultConfig = {
|
|
6
|
+
showLogout: true,
|
|
7
|
+
tabs: [
|
|
8
|
+
{
|
|
9
|
+
id: 'dashboard',
|
|
10
|
+
type: 'link',
|
|
11
|
+
href: '/',
|
|
12
|
+
icon: 'House',
|
|
13
|
+
label: {
|
|
14
|
+
en: 'Dashboard',
|
|
15
|
+
uk: 'Головна'
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
id: 'default',
|
|
20
|
+
type: 'tab',
|
|
21
|
+
icon: 'LayoutGrid',
|
|
22
|
+
label: {
|
|
23
|
+
en: 'Collections',
|
|
24
|
+
uk: 'Колекції'
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
]
|
|
28
|
+
};
|
|
29
|
+
export const payloadEnhancedSidebar = (pluginOptions = {})=>(config)=>{
|
|
30
|
+
if (pluginOptions.disabled) {
|
|
31
|
+
return config;
|
|
32
|
+
}
|
|
33
|
+
// Merge user config with defaults
|
|
34
|
+
const sidebarConfig = {
|
|
35
|
+
...defaultConfig,
|
|
36
|
+
...pluginOptions,
|
|
37
|
+
tabs: pluginOptions.tabs ?? defaultConfig.tabs
|
|
38
|
+
};
|
|
39
|
+
if (!config.admin) {
|
|
40
|
+
config.admin = {};
|
|
41
|
+
}
|
|
42
|
+
if (!config.admin.components) {
|
|
43
|
+
config.admin.components = {};
|
|
44
|
+
}
|
|
45
|
+
if (!config.admin.components.Nav) {
|
|
46
|
+
config.admin.components.Nav = {
|
|
47
|
+
path: '@veiag/payload-enhanced-sidebar/rsc#EnhancedSidebar',
|
|
48
|
+
serverProps: {
|
|
49
|
+
sidebarConfig
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
// Adding translations
|
|
54
|
+
if (!config.i18n) {
|
|
55
|
+
config.i18n = {};
|
|
56
|
+
}
|
|
57
|
+
if (!config.i18n.translations) {
|
|
58
|
+
config.i18n.translations = {};
|
|
59
|
+
}
|
|
60
|
+
config.i18n.translations = deepMerge(config.i18n.translations, sidebarTranslations);
|
|
61
|
+
return config;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { type Config, deepMerge } from 'payload'\n\nimport type { EnhancedSidebarConfig } from './types'\n\nimport { sidebarTranslations } from './translations'\n\n/**\n * Default configuration for the enhanced sidebar\n */\nconst defaultConfig: EnhancedSidebarConfig = {\n showLogout: true,\n tabs: [\n {\n id: 'dashboard',\n type: 'link',\n href: '/',\n icon: 'House',\n label: { en: 'Dashboard', uk: 'Головна' },\n },\n {\n id: 'default',\n type: 'tab',\n icon: 'LayoutGrid',\n label: { en: 'Collections', uk: 'Колекції' },\n },\n ],\n}\n\nexport const payloadEnhancedSidebar =\n (pluginOptions: EnhancedSidebarConfig = {}) =>\n (config: Config): Config => {\n if (pluginOptions.disabled) {\n return config\n }\n\n // Merge user config with defaults\n const sidebarConfig: EnhancedSidebarConfig = {\n ...defaultConfig,\n ...pluginOptions,\n tabs: pluginOptions.tabs ?? defaultConfig.tabs,\n }\n\n if (!config.admin) {\n config.admin = {}\n }\n\n if (!config.admin.components) {\n config.admin.components = {}\n }\n\n if (!config.admin.components.Nav) {\n config.admin.components.Nav = {\n path: '@veiag/payload-enhanced-sidebar/rsc#EnhancedSidebar',\n serverProps: {\n sidebarConfig,\n },\n }\n }\n\n // Adding translations\n if (!config.i18n) {\n config.i18n = {}\n }\n if (!config.i18n.translations) {\n config.i18n.translations = {}\n }\n\n config.i18n.translations = deepMerge(config.i18n.translations, sidebarTranslations)\n\n return config\n }\n\nexport type { EnhancedSidebarConfig } from './types'\n"],"names":["deepMerge","sidebarTranslations","defaultConfig","showLogout","tabs","id","type","href","icon","label","en","uk","payloadEnhancedSidebar","pluginOptions","config","disabled","sidebarConfig","admin","components","Nav","path","serverProps","i18n","translations"],"mappings":"AAAA,SAAsBA,SAAS,QAAQ,UAAS;AAIhD,SAASC,mBAAmB,QAAQ,iBAAgB;AAEpD;;CAEC,GACD,MAAMC,gBAAuC;IAC3CC,YAAY;IACZC,MAAM;QACJ;YACEC,IAAI;YACJC,MAAM;YACNC,MAAM;YACNC,MAAM;YACNC,OAAO;gBAAEC,IAAI;gBAAaC,IAAI;YAAU;QAC1C;QACA;YACEN,IAAI;YACJC,MAAM;YACNE,MAAM;YACNC,OAAO;gBAAEC,IAAI;gBAAeC,IAAI;YAAW;QAC7C;KACD;AACH;AAEA,OAAO,MAAMC,yBACX,CAACC,gBAAuC,CAAC,CAAC,GAC1C,CAACC;QACC,IAAID,cAAcE,QAAQ,EAAE;YAC1B,OAAOD;QACT;QAEA,kCAAkC;QAClC,MAAME,gBAAuC;YAC3C,GAAGd,aAAa;YAChB,GAAGW,aAAa;YAChBT,MAAMS,cAAcT,IAAI,IAAIF,cAAcE,IAAI;QAChD;QAEA,IAAI,CAACU,OAAOG,KAAK,EAAE;YACjBH,OAAOG,KAAK,GAAG,CAAC;QAClB;QAEA,IAAI,CAACH,OAAOG,KAAK,CAACC,UAAU,EAAE;YAC5BJ,OAAOG,KAAK,CAACC,UAAU,GAAG,CAAC;QAC7B;QAEA,IAAI,CAACJ,OAAOG,KAAK,CAACC,UAAU,CAACC,GAAG,EAAE;YAChCL,OAAOG,KAAK,CAACC,UAAU,CAACC,GAAG,GAAG;gBAC5BC,MAAM;gBACNC,aAAa;oBACXL;gBACF;YACF;QACF;QAEA,sBAAsB;QACtB,IAAI,CAACF,OAAOQ,IAAI,EAAE;YAChBR,OAAOQ,IAAI,GAAG,CAAC;QACjB;QACA,IAAI,CAACR,OAAOQ,IAAI,CAACC,YAAY,EAAE;YAC7BT,OAAOQ,IAAI,CAACC,YAAY,GAAG,CAAC;QAC9B;QAEAT,OAAOQ,IAAI,CAACC,YAAY,GAAGvB,UAAUc,OAAOQ,IAAI,CAACC,YAAY,EAAEtB;QAE/D,OAAOa;IACT,EAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { NestedKeysStripped } from '@payloadcms/translations';
|
|
2
|
+
import type { enTranslations } from '@payloadcms/translations/languages/en';
|
|
3
|
+
export declare const sidebarTranslations: {
|
|
4
|
+
en: {
|
|
5
|
+
sidebarPlugin: {
|
|
6
|
+
collections: string;
|
|
7
|
+
dashboard: string;
|
|
8
|
+
logout: string;
|
|
9
|
+
};
|
|
10
|
+
};
|
|
11
|
+
uk: {
|
|
12
|
+
sidebarPlugin: {
|
|
13
|
+
collections: string;
|
|
14
|
+
dashboard: string;
|
|
15
|
+
logout: string;
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
export type AvaibleTranslation = keyof typeof sidebarTranslations.en.sidebarPlugin;
|
|
20
|
+
export type CustomTranslationsObject = typeof enTranslations & typeof sidebarTranslations.en;
|
|
21
|
+
export type CustomTranslationsKeys = NestedKeysStripped<CustomTranslationsObject>;
|
|
22
|
+
export declare const mergeTranslations: (existingTranslations: Record<string, unknown>, newTranslations: Record<string, unknown>) => Record<string, unknown>;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
//TODO: find suitable translations in Payload core to avoid duplication
|
|
2
|
+
// Don't think we need translations at all
|
|
3
|
+
export const sidebarTranslations = {
|
|
4
|
+
en: {
|
|
5
|
+
sidebarPlugin: {
|
|
6
|
+
collections: 'Collections',
|
|
7
|
+
dashboard: 'Dashboard',
|
|
8
|
+
logout: 'Logout'
|
|
9
|
+
}
|
|
10
|
+
},
|
|
11
|
+
uk: {
|
|
12
|
+
sidebarPlugin: {
|
|
13
|
+
collections: 'Колекції',
|
|
14
|
+
dashboard: 'Головна',
|
|
15
|
+
logout: 'Вийти'
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
export const mergeTranslations = (existingTranslations, newTranslations)=>{
|
|
20
|
+
return {
|
|
21
|
+
...existingTranslations,
|
|
22
|
+
...newTranslations,
|
|
23
|
+
sidebarTranslations: {
|
|
24
|
+
...existingTranslations.sidebarTranslations || {},
|
|
25
|
+
...newTranslations.sidebarTranslations || {}
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/translations/index.ts"],"sourcesContent":["import type { NestedKeysStripped } from '@payloadcms/translations'\nimport type { enTranslations } from '@payloadcms/translations/languages/en'\n\n//TODO: find suitable translations in Payload core to avoid duplication\n// Don't think we need translations at all\nexport const sidebarTranslations = {\n en: {\n sidebarPlugin: {\n collections: 'Collections',\n dashboard: 'Dashboard',\n logout: 'Logout',\n },\n },\n uk: {\n sidebarPlugin: {\n collections: 'Колекції',\n dashboard: 'Головна',\n logout: 'Вийти',\n },\n },\n}\n\nexport type AvaibleTranslation = keyof typeof sidebarTranslations.en.sidebarPlugin\n\nexport type CustomTranslationsObject = typeof enTranslations & typeof sidebarTranslations.en\n\nexport type CustomTranslationsKeys = NestedKeysStripped<CustomTranslationsObject>\n\nexport const mergeTranslations = (\n existingTranslations: Record<string, unknown>,\n newTranslations: Record<string, unknown>,\n): Record<string, unknown> => {\n return {\n ...existingTranslations,\n ...newTranslations,\n sidebarTranslations: {\n ...(existingTranslations.sidebarTranslations || {}),\n ...(newTranslations.sidebarTranslations || {}),\n },\n }\n}\n"],"names":["sidebarTranslations","en","sidebarPlugin","collections","dashboard","logout","uk","mergeTranslations","existingTranslations","newTranslations"],"mappings":"AAGA,uEAAuE;AACvE,0CAA0C;AAC1C,OAAO,MAAMA,sBAAsB;IACjCC,IAAI;QACFC,eAAe;YACbC,aAAa;YACbC,WAAW;YACXC,QAAQ;QACV;IACF;IACAC,IAAI;QACFJ,eAAe;YACbC,aAAa;YACbC,WAAW;YACXC,QAAQ;QACV;IACF;AACF,EAAC;AAQD,OAAO,MAAME,oBAAoB,CAC/BC,sBACAC;IAEA,OAAO;QACL,GAAGD,oBAAoB;QACvB,GAAGC,eAAe;QAClBT,qBAAqB;YACnB,GAAIQ,qBAAqBR,mBAAmB,IAAI,CAAC,CAAC;YAClD,GAAIS,gBAAgBT,mBAAmB,IAAI,CAAC,CAAC;QAC/C;IACF;AACF,EAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import type { icons, LucideIcon } from 'lucide-react';
|
|
2
|
+
import type { CollectionSlug, GlobalSlug } from 'payload';
|
|
3
|
+
export type IconName = keyof typeof icons;
|
|
4
|
+
export type LocalizedString = {
|
|
5
|
+
[locale: string]: string;
|
|
6
|
+
} | string;
|
|
7
|
+
export type InternalIcon = IconName | LucideIcon;
|
|
8
|
+
/**
|
|
9
|
+
* Sidebar tab that shows content when selected
|
|
10
|
+
*/
|
|
11
|
+
export interface SidebarTabContent {
|
|
12
|
+
/**
|
|
13
|
+
* Collections to show in this tab.
|
|
14
|
+
* If not specified, no collections are shown (unless items are specified).
|
|
15
|
+
* Use collection slugs.
|
|
16
|
+
*/
|
|
17
|
+
collections?: CollectionSlug[];
|
|
18
|
+
/**
|
|
19
|
+
* Custom items to add to this tab.
|
|
20
|
+
* Items with `group` will be merged into matching collection groups.
|
|
21
|
+
* Items without `group` will be shown at the bottom as a flat list.
|
|
22
|
+
*/
|
|
23
|
+
customItems?: SidebarTabItem[];
|
|
24
|
+
/**
|
|
25
|
+
* Globals to show in this tab.
|
|
26
|
+
* If not specified, no globals are shown.
|
|
27
|
+
* Use global slugs.
|
|
28
|
+
*/
|
|
29
|
+
globals?: GlobalSlug[];
|
|
30
|
+
/** Icon name from lucide-react */
|
|
31
|
+
icon: IconName;
|
|
32
|
+
/** Unique identifier for the tab */
|
|
33
|
+
id: string;
|
|
34
|
+
/** Tooltip/label for the tab */
|
|
35
|
+
label: LocalizedString;
|
|
36
|
+
type: 'tab';
|
|
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;
|
|
44
|
+
/** Icon name from lucide-react */
|
|
45
|
+
icon: IconName;
|
|
46
|
+
/** Unique identifier */
|
|
47
|
+
id: string;
|
|
48
|
+
/** Tooltip/label */
|
|
49
|
+
label: LocalizedString;
|
|
50
|
+
type: 'link';
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* A tab or link in the sidebar tabs bar
|
|
54
|
+
*/
|
|
55
|
+
export type SidebarTab = SidebarTabContent | SidebarTabLink;
|
|
56
|
+
interface BaseSidebarTabItem {
|
|
57
|
+
/**
|
|
58
|
+
* Group to add this item to.
|
|
59
|
+
* If matches an existing collection group label, item will be merged into that group.
|
|
60
|
+
* If no match found, a new group will be created.
|
|
61
|
+
* If not specified, item will be shown at the bottom as ungrouped.
|
|
62
|
+
*/
|
|
63
|
+
group?: LocalizedString;
|
|
64
|
+
/** Display label */
|
|
65
|
+
label: LocalizedString;
|
|
66
|
+
/** Unique slug for the item */
|
|
67
|
+
slug: string;
|
|
68
|
+
}
|
|
69
|
+
interface ExternalHrefItem extends BaseSidebarTabItem {
|
|
70
|
+
/** Link href (absolute URL or relative to root) */
|
|
71
|
+
href: string;
|
|
72
|
+
/** Whether the link is external, without admin route prefix. */
|
|
73
|
+
isExternal: true;
|
|
74
|
+
}
|
|
75
|
+
interface InternalHrefItem extends BaseSidebarTabItem {
|
|
76
|
+
/** Link href (relative to admin route) */
|
|
77
|
+
href: '' | `/${string}`;
|
|
78
|
+
/** Whether the link is external, without admin route prefix. */
|
|
79
|
+
isExternal?: false;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Custom item inside a sidebar tab
|
|
83
|
+
*/
|
|
84
|
+
export type SidebarTabItem = ExternalHrefItem | InternalHrefItem;
|
|
85
|
+
/**
|
|
86
|
+
* Configuration for the enhanced sidebar
|
|
87
|
+
*/
|
|
88
|
+
export interface EnhancedSidebarConfig {
|
|
89
|
+
/**
|
|
90
|
+
* Disable the plugin
|
|
91
|
+
* @default false
|
|
92
|
+
*/
|
|
93
|
+
disabled?: boolean;
|
|
94
|
+
/**
|
|
95
|
+
* Custom icons for collections and globals in the default tab.
|
|
96
|
+
*/
|
|
97
|
+
icons?: {
|
|
98
|
+
collections?: Partial<Record<CollectionSlug, IconName>>;
|
|
99
|
+
globals?: Partial<Record<GlobalSlug, IconName>>;
|
|
100
|
+
};
|
|
101
|
+
/**
|
|
102
|
+
* Show logout button at the bottom of the tabs bar.
|
|
103
|
+
* @default true
|
|
104
|
+
*/
|
|
105
|
+
showLogout?: boolean;
|
|
106
|
+
/**
|
|
107
|
+
* Tabs and links to show in the sidebar tabs bar.
|
|
108
|
+
* Order matters - items are rendered top to bottom.
|
|
109
|
+
*
|
|
110
|
+
* @default [{ type: 'tab', id: 'default', icon: 'LayoutGrid', label: 'Collections' }]
|
|
111
|
+
*/
|
|
112
|
+
tabs?: SidebarTab[];
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Generic document type for collections, with dynamic keys.
|
|
116
|
+
* We assume values are either string or number for simplicity, useAsTitle is making sure of that.
|
|
117
|
+
*/
|
|
118
|
+
export type GenericCollectionDocument = {
|
|
119
|
+
[key: string]: number | string;
|
|
120
|
+
id: string;
|
|
121
|
+
};
|
|
122
|
+
export type ExtendedEntity = {
|
|
123
|
+
href?: string;
|
|
124
|
+
label: Record<string, string> | string;
|
|
125
|
+
slug: string;
|
|
126
|
+
type: 'collection' | 'custom' | 'global';
|
|
127
|
+
};
|
|
128
|
+
export type ExtendedGroup = {
|
|
129
|
+
entities: ExtendedEntity[];
|
|
130
|
+
label: Record<string, string> | string;
|
|
131
|
+
};
|
|
132
|
+
export {};
|
package/dist/types.js
ADDED
|
@@ -0,0 +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\n/**\n * Sidebar link that navigates to a URL (not a tab)\n */\nexport interface SidebarTabLink {\n /** Link href (relative to admin route) */\n href: string\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}\n\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\nexport type ExtendedEntity = {\n href?: string\n label: Record<string, string> | string\n slug: string\n type: 'collection' | 'custom' | 'global'\n}\n\nexport type ExtendedGroup = {\n entities: ExtendedEntity[]\n label: Record<string, string> | string\n}\n"],"names":[],"mappings":"AAgJA,WAGC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export const convertSlugToTitle = (slug)=>{
|
|
2
|
+
return slug.replace(/-/g, ' ').replace(/\b\w/g, (char)=>char.toUpperCase());
|
|
3
|
+
};
|
|
4
|
+
export const extractLocalizedValue = (value, locale, fallbackSlug)=>{
|
|
5
|
+
if (!value) {
|
|
6
|
+
return fallbackSlug ? convertSlugToTitle(fallbackSlug) : '';
|
|
7
|
+
}
|
|
8
|
+
if (typeof value === 'string') {
|
|
9
|
+
return value;
|
|
10
|
+
}
|
|
11
|
+
return value[locale] || Object.values(value)[0] || (fallbackSlug ? convertSlugToTitle(fallbackSlug) : '');
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/utils/index.ts"],"sourcesContent":["import type { LocalizedString } from '../types'\n\nexport const convertSlugToTitle = (slug: string): string => {\n return slug.replace(/-/g, ' ').replace(/\\b\\w/g, (char) => char.toUpperCase())\n}\n\nexport const extractLocalizedValue = (\n value: LocalizedString | undefined,\n locale: string,\n fallbackSlug?: string,\n): string => {\n if (!value) {\n return fallbackSlug ? convertSlugToTitle(fallbackSlug) : ''\n }\n if (typeof value === 'string') {\n return value\n }\n return value[locale] || Object.values(value)[0] || (fallbackSlug ? convertSlugToTitle(fallbackSlug) : '')\n}\n"],"names":["convertSlugToTitle","slug","replace","char","toUpperCase","extractLocalizedValue","value","locale","fallbackSlug","Object","values"],"mappings":"AAEA,OAAO,MAAMA,qBAAqB,CAACC;IACjC,OAAOA,KAAKC,OAAO,CAAC,MAAM,KAAKA,OAAO,CAAC,SAAS,CAACC,OAASA,KAAKC,WAAW;AAC5E,EAAC;AAED,OAAO,MAAMC,wBAAwB,CACnCC,OACAC,QACAC;IAEA,IAAI,CAACF,OAAO;QACV,OAAOE,eAAeR,mBAAmBQ,gBAAgB;IAC3D;IACA,IAAI,OAAOF,UAAU,UAAU;QAC7B,OAAOA;IACT;IACA,OAAOA,KAAK,CAACC,OAAO,IAAIE,OAAOC,MAAM,CAACJ,MAAM,CAAC,EAAE,IAAKE,CAAAA,eAAeR,mBAAmBQ,gBAAgB,EAAC;AACzG,EAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@veiag/payload-enhanced-sidebar",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "An enhanced sidebar plugin for Payload CMS with tabbed navigation to organize collections and globals.",
|
|
5
|
+
"author": "VeiaG",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"default": "./dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"./client": {
|
|
15
|
+
"import": "./dist/exports/client.js",
|
|
16
|
+
"types": "./dist/exports/client.d.ts",
|
|
17
|
+
"default": "./dist/exports/client.js"
|
|
18
|
+
},
|
|
19
|
+
"./rsc": {
|
|
20
|
+
"import": "./dist/exports/rsc.js",
|
|
21
|
+
"types": "./dist/exports/rsc.d.ts",
|
|
22
|
+
"default": "./dist/exports/rsc.js"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"main": "./dist/index.js",
|
|
26
|
+
"types": "./dist/index.d.ts",
|
|
27
|
+
"files": [
|
|
28
|
+
"dist"
|
|
29
|
+
],
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@eslint/eslintrc": "^3.2.0",
|
|
32
|
+
"@payloadcms/db-mongodb": "3.37.0",
|
|
33
|
+
"@payloadcms/db-postgres": "3.37.0",
|
|
34
|
+
"@payloadcms/db-sqlite": "3.37.0",
|
|
35
|
+
"@payloadcms/eslint-config": "3.9.0",
|
|
36
|
+
"@payloadcms/next": "3.37.0",
|
|
37
|
+
"@payloadcms/richtext-lexical": "3.37.0",
|
|
38
|
+
"@payloadcms/ui": "3.37.0",
|
|
39
|
+
"@playwright/test": "1.56.1",
|
|
40
|
+
"@swc-node/register": "1.10.9",
|
|
41
|
+
"@swc/cli": "0.6.0",
|
|
42
|
+
"@types/node": "^22.5.4",
|
|
43
|
+
"@types/react": "19.2.1",
|
|
44
|
+
"@types/react-dom": "19.2.1",
|
|
45
|
+
"copyfiles": "2.4.1",
|
|
46
|
+
"cross-env": "^7.0.3",
|
|
47
|
+
"eslint": "^9.23.0",
|
|
48
|
+
"eslint-config-next": "15.4.7",
|
|
49
|
+
"graphql": "^16.8.1",
|
|
50
|
+
"mongodb-memory-server": "10.1.4",
|
|
51
|
+
"next": "15.4.8",
|
|
52
|
+
"open": "^10.1.0",
|
|
53
|
+
"payload": "3.37.0",
|
|
54
|
+
"prettier": "^3.4.2",
|
|
55
|
+
"qs-esm": "7.0.2",
|
|
56
|
+
"react": "19.2.1",
|
|
57
|
+
"react-dom": "19.2.1",
|
|
58
|
+
"rimraf": "3.0.2",
|
|
59
|
+
"sharp": "0.34.2",
|
|
60
|
+
"sort-package-json": "^2.10.0",
|
|
61
|
+
"typescript": "5.7.3",
|
|
62
|
+
"vite-tsconfig-paths": "^5.1.4",
|
|
63
|
+
"vitest": "^3.1.2"
|
|
64
|
+
},
|
|
65
|
+
"peerDependencies": {
|
|
66
|
+
"payload": "^3.37.0"
|
|
67
|
+
},
|
|
68
|
+
"engines": {
|
|
69
|
+
"node": "^18.20.2 || >=20.9.0",
|
|
70
|
+
"pnpm": "^9 || ^10"
|
|
71
|
+
},
|
|
72
|
+
"registry": "https://registry.npmjs.org/",
|
|
73
|
+
"dependencies": {
|
|
74
|
+
"@payloadcms/translations": "^3.37.0",
|
|
75
|
+
"lucide-react": "^0.556.0"
|
|
76
|
+
},
|
|
77
|
+
"scripts": {
|
|
78
|
+
"build": "pnpm copyfiles && pnpm build:types && pnpm build:swc",
|
|
79
|
+
"build:swc": "swc ./src -d ./dist --config-file .swcrc --strip-leading-paths",
|
|
80
|
+
"build:types": "tsc --outDir dist --rootDir ./src",
|
|
81
|
+
"clean": "rimraf {dist,*.tsbuildinfo}",
|
|
82
|
+
"copyfiles": "copyfiles -u 1 \"src/**/*.{html,css,scss,ttf,woff,woff2,eot,svg,jpg,png,json}\" dist/",
|
|
83
|
+
"dev": "next dev dev --turbo",
|
|
84
|
+
"dev:generate-importmap": "pnpm dev:payload generate:importmap",
|
|
85
|
+
"dev:generate-types": "pnpm dev:payload generate:types",
|
|
86
|
+
"dev:payload": "cross-env PAYLOAD_CONFIG_PATH=./dev/payload.config.ts payload",
|
|
87
|
+
"generate:importmap": "pnpm dev:generate-importmap",
|
|
88
|
+
"generate:types": "pnpm dev:generate-types",
|
|
89
|
+
"lint": "eslint",
|
|
90
|
+
"lint:fix": "eslint ./src --fix",
|
|
91
|
+
"test": "pnpm test:int && pnpm test:e2e",
|
|
92
|
+
"test:e2e": "playwright test",
|
|
93
|
+
"test:int": "vitest"
|
|
94
|
+
}
|
|
95
|
+
}
|