@veiag/payload-cmdk 1.0.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 +594 -0
- package/dist/components/CommandMenuContext.d.ts +15 -0
- package/dist/components/CommandMenuContext.js +430 -0
- package/dist/components/CommandMenuContext.js.map +1 -0
- package/dist/components/SearchButton.d.ts +8 -0
- package/dist/components/SearchButton.js +106 -0
- package/dist/components/SearchButton.js.map +1 -0
- package/dist/components/SearchButton.scss +133 -0
- package/dist/components/cmdk/command.scss +334 -0
- package/dist/components/cmdk/index.d.ts +12 -0
- package/dist/components/cmdk/index.js +77 -0
- package/dist/components/cmdk/index.js.map +1 -0
- package/dist/components/modal.scss +94 -0
- package/dist/endpoints/customEndpointHandler.d.ts +2 -0
- package/dist/endpoints/customEndpointHandler.js +7 -0
- package/dist/endpoints/customEndpointHandler.js.map +1 -0
- package/dist/exports/client.d.ts +2 -0
- package/dist/exports/client.js +4 -0
- package/dist/exports/client.js.map +1 -0
- package/dist/exports/rsc.d.ts +0 -0
- package/dist/exports/rsc.js +2 -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 +3 -0
- package/dist/index.js +74 -0
- package/dist/index.js.map +1 -0
- package/dist/translations/index.d.ts +32 -0
- package/dist/translations/index.js +38 -0
- package/dist/translations/index.js.map +1 -0
- package/dist/types.d.ts +223 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/index.d.ts +30 -0
- package/dist/utils/index.js +191 -0
- package/dist/utils/index.js.map +1 -0
- package/package.json +126 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/exports/rsc.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const useMutationObserver: (ref: React.RefObject<HTMLElement | null>, callback: MutationCallback, options?: MutationObserverInit) => void;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
export const useMutationObserver = (ref, callback, options = {
|
|
3
|
+
attributes: true,
|
|
4
|
+
characterData: true,
|
|
5
|
+
childList: true,
|
|
6
|
+
subtree: true
|
|
7
|
+
})=>{
|
|
8
|
+
useEffect(()=>{
|
|
9
|
+
if (ref.current) {
|
|
10
|
+
const observer = new MutationObserver(callback);
|
|
11
|
+
observer.observe(ref.current, options);
|
|
12
|
+
return ()=>observer.disconnect();
|
|
13
|
+
}
|
|
14
|
+
}, [
|
|
15
|
+
ref,
|
|
16
|
+
callback,
|
|
17
|
+
options
|
|
18
|
+
]);
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
//# sourceMappingURL=useMutationObserver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/hooks/useMutationObserver.ts"],"sourcesContent":["import { useEffect } from 'react'\n\nexport const useMutationObserver = (\n ref: React.RefObject<HTMLElement | null>,\n callback: MutationCallback,\n options: MutationObserverInit = {\n attributes: true,\n characterData: true,\n childList: true,\n subtree: true,\n },\n) => {\n useEffect(() => {\n if (ref.current) {\n const observer = new MutationObserver(callback)\n observer.observe(ref.current, options)\n return () => observer.disconnect()\n }\n }, [ref, callback, options])\n}\n"],"names":["useEffect","useMutationObserver","ref","callback","options","attributes","characterData","childList","subtree","current","observer","MutationObserver","observe","disconnect"],"mappings":"AAAA,SAASA,SAAS,QAAQ,QAAO;AAEjC,OAAO,MAAMC,sBAAsB,CACjCC,KACAC,UACAC,UAAgC;IAC9BC,YAAY;IACZC,eAAe;IACfC,WAAW;IACXC,SAAS;AACX,CAAC;IAEDR,UAAU;QACR,IAAIE,IAAIO,OAAO,EAAE;YACf,MAAMC,WAAW,IAAIC,iBAAiBR;YACtCO,SAASE,OAAO,CAACV,IAAIO,OAAO,EAAEL;YAC9B,OAAO,IAAMM,SAASG,UAAU;QAClC;IACF,GAAG;QAACX;QAAKC;QAAUC;KAAQ;AAC7B,EAAC"}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { deepMerge } from 'payload';
|
|
2
|
+
import { commandPluginTranslations } from './translations';
|
|
3
|
+
export const payloadCmdk = (pluginOptions)=>(config)=>{
|
|
4
|
+
/**
|
|
5
|
+
* If the plugin is disabled, we still want to keep added collections/fields so the database schema is consistent which is important for migrations.
|
|
6
|
+
* If your plugin heavily modifies the database schema, you may want to remove this property.
|
|
7
|
+
*/ if (pluginOptions.disabled) {
|
|
8
|
+
return config;
|
|
9
|
+
}
|
|
10
|
+
if (!config.admin) {
|
|
11
|
+
config.admin = {};
|
|
12
|
+
}
|
|
13
|
+
if (!config.admin.components) {
|
|
14
|
+
config.admin.components = {};
|
|
15
|
+
}
|
|
16
|
+
if (!config.admin.components.providers) {
|
|
17
|
+
config.admin.components.providers = [];
|
|
18
|
+
}
|
|
19
|
+
config.admin.components.providers.push({
|
|
20
|
+
clientProps: {
|
|
21
|
+
pluginConfig: pluginOptions
|
|
22
|
+
},
|
|
23
|
+
path: '@veiag/payload-cmdk/client#CommandMenuProvider'
|
|
24
|
+
});
|
|
25
|
+
if (pluginOptions.searchButton !== false) {
|
|
26
|
+
let searchButtonPosition = 'actions';
|
|
27
|
+
if (pluginOptions.searchButton && pluginOptions.searchButton.position) {
|
|
28
|
+
searchButtonPosition = pluginOptions.searchButton.position;
|
|
29
|
+
}
|
|
30
|
+
if (searchButtonPosition === 'nav') {
|
|
31
|
+
if (!config.admin.components.beforeNavLinks) {
|
|
32
|
+
config.admin.components.beforeNavLinks = [];
|
|
33
|
+
}
|
|
34
|
+
config.admin.components.beforeNavLinks = [
|
|
35
|
+
{
|
|
36
|
+
clientProps: {
|
|
37
|
+
position: pluginOptions.searchButton?.position || 'nav',
|
|
38
|
+
shortcut: pluginOptions.shortcut || [
|
|
39
|
+
'meta+k',
|
|
40
|
+
'ctrl+k'
|
|
41
|
+
]
|
|
42
|
+
},
|
|
43
|
+
path: '@veiag/payload-cmdk/client#SearchButton'
|
|
44
|
+
},
|
|
45
|
+
...config.admin.components.beforeNavLinks
|
|
46
|
+
];
|
|
47
|
+
} else {
|
|
48
|
+
if (!config.admin.components.actions) {
|
|
49
|
+
config.admin.components.actions = [];
|
|
50
|
+
}
|
|
51
|
+
config.admin.components.actions.push({
|
|
52
|
+
clientProps: {
|
|
53
|
+
position: pluginOptions.searchButton?.position || 'actions',
|
|
54
|
+
shortcut: pluginOptions.shortcut || [
|
|
55
|
+
'meta+k',
|
|
56
|
+
'ctrl+k'
|
|
57
|
+
]
|
|
58
|
+
},
|
|
59
|
+
path: '@veiag/payload-cmdk/client#SearchButton'
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
//Adding translations
|
|
64
|
+
if (!config.i18n) {
|
|
65
|
+
config.i18n = {};
|
|
66
|
+
}
|
|
67
|
+
if (!config.i18n.translations) {
|
|
68
|
+
config.i18n.translations = {};
|
|
69
|
+
}
|
|
70
|
+
config.i18n.translations = deepMerge(config.i18n.translations, commandPluginTranslations);
|
|
71
|
+
return config;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { type Config, deepMerge } from 'payload'\n\nimport type { PluginCommandMenuConfig } from './types'\n\nimport { commandPluginTranslations } from './translations'\n\nexport const payloadCmdk =\n (pluginOptions: PluginCommandMenuConfig) =>\n (config: Config): Config => {\n /**\n * If the plugin is disabled, we still want to keep added collections/fields so the database schema is consistent which is important for migrations.\n * If your plugin heavily modifies the database schema, you may want to remove this property.\n */\n if (pluginOptions.disabled) {\n return config\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.providers) {\n config.admin.components.providers = []\n }\n\n config.admin.components.providers.push({\n clientProps: {\n pluginConfig: pluginOptions,\n },\n path: '@veiag/payload-cmdk/client#CommandMenuProvider',\n })\n\n if (pluginOptions.searchButton !== false) {\n let searchButtonPosition = 'actions'\n if (pluginOptions.searchButton && pluginOptions.searchButton.position) {\n searchButtonPosition = pluginOptions.searchButton.position\n }\n if (searchButtonPosition === 'nav') {\n if (!config.admin.components.beforeNavLinks) {\n config.admin.components.beforeNavLinks = []\n }\n config.admin.components.beforeNavLinks = [\n {\n clientProps: {\n position: pluginOptions.searchButton?.position || 'nav',\n shortcut: pluginOptions.shortcut || ['meta+k', 'ctrl+k'],\n },\n path: '@veiag/payload-cmdk/client#SearchButton',\n },\n ...config.admin.components.beforeNavLinks,\n ]\n } else {\n if (!config.admin.components.actions) {\n config.admin.components.actions = []\n }\n config.admin.components.actions.push({\n clientProps: {\n position: pluginOptions.searchButton?.position || 'actions',\n shortcut: pluginOptions.shortcut || ['meta+k', 'ctrl+k'],\n },\n path: '@veiag/payload-cmdk/client#SearchButton',\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, commandPluginTranslations)\n\n return config\n }\n"],"names":["deepMerge","commandPluginTranslations","payloadCmdk","pluginOptions","config","disabled","admin","components","providers","push","clientProps","pluginConfig","path","searchButton","searchButtonPosition","position","beforeNavLinks","shortcut","actions","i18n","translations"],"mappings":"AAAA,SAAsBA,SAAS,QAAQ,UAAS;AAIhD,SAASC,yBAAyB,QAAQ,iBAAgB;AAE1D,OAAO,MAAMC,cACX,CAACC,gBACD,CAACC;QACC;;;KAGC,GACD,IAAID,cAAcE,QAAQ,EAAE;YAC1B,OAAOD;QACT;QAEA,IAAI,CAACA,OAAOE,KAAK,EAAE;YACjBF,OAAOE,KAAK,GAAG,CAAC;QAClB;QAEA,IAAI,CAACF,OAAOE,KAAK,CAACC,UAAU,EAAE;YAC5BH,OAAOE,KAAK,CAACC,UAAU,GAAG,CAAC;QAC7B;QAEA,IAAI,CAACH,OAAOE,KAAK,CAACC,UAAU,CAACC,SAAS,EAAE;YACtCJ,OAAOE,KAAK,CAACC,UAAU,CAACC,SAAS,GAAG,EAAE;QACxC;QAEAJ,OAAOE,KAAK,CAACC,UAAU,CAACC,SAAS,CAACC,IAAI,CAAC;YACrCC,aAAa;gBACXC,cAAcR;YAChB;YACAS,MAAM;QACR;QAEA,IAAIT,cAAcU,YAAY,KAAK,OAAO;YACxC,IAAIC,uBAAuB;YAC3B,IAAIX,cAAcU,YAAY,IAAIV,cAAcU,YAAY,CAACE,QAAQ,EAAE;gBACrED,uBAAuBX,cAAcU,YAAY,CAACE,QAAQ;YAC5D;YACA,IAAID,yBAAyB,OAAO;gBAClC,IAAI,CAACV,OAAOE,KAAK,CAACC,UAAU,CAACS,cAAc,EAAE;oBAC3CZ,OAAOE,KAAK,CAACC,UAAU,CAACS,cAAc,GAAG,EAAE;gBAC7C;gBACAZ,OAAOE,KAAK,CAACC,UAAU,CAACS,cAAc,GAAG;oBACvC;wBACEN,aAAa;4BACXK,UAAUZ,cAAcU,YAAY,EAAEE,YAAY;4BAClDE,UAAUd,cAAcc,QAAQ,IAAI;gCAAC;gCAAU;6BAAS;wBAC1D;wBACAL,MAAM;oBACR;uBACGR,OAAOE,KAAK,CAACC,UAAU,CAACS,cAAc;iBAC1C;YACH,OAAO;gBACL,IAAI,CAACZ,OAAOE,KAAK,CAACC,UAAU,CAACW,OAAO,EAAE;oBACpCd,OAAOE,KAAK,CAACC,UAAU,CAACW,OAAO,GAAG,EAAE;gBACtC;gBACAd,OAAOE,KAAK,CAACC,UAAU,CAACW,OAAO,CAACT,IAAI,CAAC;oBACnCC,aAAa;wBACXK,UAAUZ,cAAcU,YAAY,EAAEE,YAAY;wBAClDE,UAAUd,cAAcc,QAAQ,IAAI;4BAAC;4BAAU;yBAAS;oBAC1D;oBACAL,MAAM;gBACR;YACF;QACF;QAEA,qBAAqB;QACrB,IAAI,CAACR,OAAOe,IAAI,EAAE;YAChBf,OAAOe,IAAI,GAAG,CAAC;QACjB;QACA,IAAI,CAACf,OAAOe,IAAI,CAACC,YAAY,EAAE;YAC7BhB,OAAOe,IAAI,CAACC,YAAY,GAAG,CAAC;QAC9B;QAEAhB,OAAOe,IAAI,CAACC,YAAY,GAAGpB,UAAUI,OAAOe,IAAI,CAACC,YAAY,EAAEnB;QAE/D,OAAOG;IACT,EAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { NestedKeysStripped } from '@payloadcms/translations';
|
|
2
|
+
import type { enTranslations } from '@payloadcms/translations/languages/en';
|
|
3
|
+
export declare const commandPluginTranslations: {
|
|
4
|
+
en: {
|
|
5
|
+
cmdkPlugin: {
|
|
6
|
+
loading: string;
|
|
7
|
+
navigate: string;
|
|
8
|
+
noResults: string;
|
|
9
|
+
open: string;
|
|
10
|
+
search: string;
|
|
11
|
+
searchIn: string;
|
|
12
|
+
searchInCollection: string;
|
|
13
|
+
searchShort: string;
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
uk: {
|
|
17
|
+
cmdkPlugin: {
|
|
18
|
+
loading: string;
|
|
19
|
+
navigate: string;
|
|
20
|
+
noResults: string;
|
|
21
|
+
open: string;
|
|
22
|
+
search: string;
|
|
23
|
+
searchIn: string;
|
|
24
|
+
searchInCollection: string;
|
|
25
|
+
searchShort: string;
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
export type AvaibleTranslation = keyof typeof commandPluginTranslations.en.cmdkPlugin;
|
|
30
|
+
export type CustomTranslationsObject = typeof commandPluginTranslations.en & typeof enTranslations;
|
|
31
|
+
export type CustomTranslationsKeys = NestedKeysStripped<CustomTranslationsObject>;
|
|
32
|
+
export declare const mergeTranslations: (existingTranslations: Record<string, unknown>, newTranslations: Record<string, unknown>) => Record<string, unknown>;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export const commandPluginTranslations = {
|
|
2
|
+
en: {
|
|
3
|
+
cmdkPlugin: {
|
|
4
|
+
loading: 'Loading...',
|
|
5
|
+
navigate: 'to navigate',
|
|
6
|
+
noResults: 'No results found',
|
|
7
|
+
open: 'to open',
|
|
8
|
+
search: 'Search collections, globals...',
|
|
9
|
+
searchIn: 'Search in {{label}}',
|
|
10
|
+
searchInCollection: 'to search in collection',
|
|
11
|
+
searchShort: 'Search'
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
uk: {
|
|
15
|
+
cmdkPlugin: {
|
|
16
|
+
loading: 'Завантаження...',
|
|
17
|
+
navigate: 'щоб перейти',
|
|
18
|
+
noResults: 'Результатів не знайдено',
|
|
19
|
+
open: 'щоб відкрити',
|
|
20
|
+
search: 'Пошук колекцій, глобалів...',
|
|
21
|
+
searchIn: 'Пошук в {{label}}',
|
|
22
|
+
searchInCollection: 'щоб шукати в колекції',
|
|
23
|
+
searchShort: 'Пошук'
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
export const mergeTranslations = (existingTranslations, newTranslations)=>{
|
|
28
|
+
return {
|
|
29
|
+
...existingTranslations,
|
|
30
|
+
...newTranslations,
|
|
31
|
+
cmdkPlugin: {
|
|
32
|
+
...existingTranslations.cmdkPlugin || {},
|
|
33
|
+
...newTranslations.cmdkPlugin || {}
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
//# 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\nexport const commandPluginTranslations = {\n en: {\n cmdkPlugin: {\n loading: 'Loading...',\n navigate: 'to navigate',\n noResults: 'No results found',\n open: 'to open',\n search: 'Search collections, globals...',\n searchIn: 'Search in {{label}}',\n searchInCollection: 'to search in collection',\n searchShort: 'Search',\n },\n },\n uk: {\n cmdkPlugin: {\n loading: 'Завантаження...',\n navigate: 'щоб перейти',\n noResults: 'Результатів не знайдено',\n open: 'щоб відкрити',\n search: 'Пошук колекцій, глобалів...',\n searchIn: 'Пошук в {{label}}',\n searchInCollection: 'щоб шукати в колекції',\n searchShort: 'Пошук',\n },\n },\n}\n\nexport type AvaibleTranslation = keyof typeof commandPluginTranslations.en.cmdkPlugin\n\nexport type CustomTranslationsObject = typeof commandPluginTranslations.en & typeof enTranslations\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 cmdkPlugin: {\n ...(existingTranslations.cmdkPlugin || {}),\n ...(newTranslations.cmdkPlugin || {}),\n },\n }\n}\n"],"names":["commandPluginTranslations","en","cmdkPlugin","loading","navigate","noResults","open","search","searchIn","searchInCollection","searchShort","uk","mergeTranslations","existingTranslations","newTranslations"],"mappings":"AAGA,OAAO,MAAMA,4BAA4B;IACvCC,IAAI;QACFC,YAAY;YACVC,SAAS;YACTC,UAAU;YACVC,WAAW;YACXC,MAAM;YACNC,QAAQ;YACRC,UAAU;YACVC,oBAAoB;YACpBC,aAAa;QACf;IACF;IACAC,IAAI;QACFT,YAAY;YACVC,SAAS;YACTC,UAAU;YACVC,WAAW;YACXC,MAAM;YACNC,QAAQ;YACRC,UAAU;YACVC,oBAAoB;YACpBC,aAAa;QACf;IACF;AACF,EAAC;AAQD,OAAO,MAAME,oBAAoB,CAC/BC,sBACAC;IAEA,OAAO;QACL,GAAGD,oBAAoB;QACvB,GAAGC,eAAe;QAClBZ,YAAY;YACV,GAAIW,qBAAqBX,UAAU,IAAI,CAAC,CAAC;YACzC,GAAIY,gBAAgBZ,UAAU,IAAI,CAAC,CAAC;QACtC;IACF;AACF,EAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import type { LucideIcon } from 'lucide-react';
|
|
2
|
+
import type { IconName } from 'lucide-react/dynamic';
|
|
3
|
+
import type { CollectionSlug, GlobalSlug } from 'payload';
|
|
4
|
+
export type LocalizedString = {
|
|
5
|
+
[locale: string]: string;
|
|
6
|
+
} | string;
|
|
7
|
+
export type InternalIcon = IconName | LucideIcon;
|
|
8
|
+
/**
|
|
9
|
+
* Custom menu item, for configuration.
|
|
10
|
+
* Will be mapped to CommandMenuItem internally.
|
|
11
|
+
*/
|
|
12
|
+
export type CustomMenuItem = {
|
|
13
|
+
action: CommandMenuAction;
|
|
14
|
+
icon?: IconName;
|
|
15
|
+
label: LocalizedString;
|
|
16
|
+
slug: string;
|
|
17
|
+
type: 'item';
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Custom menu group, for configuration.
|
|
21
|
+
* Will be mapped to CommandMenuGroup internally.
|
|
22
|
+
*
|
|
23
|
+
* Groups will be merged if they have the same title.
|
|
24
|
+
*/
|
|
25
|
+
export type CustomMenuGroup = {
|
|
26
|
+
items: CustomMenuItem[];
|
|
27
|
+
title: LocalizedString;
|
|
28
|
+
type: 'group';
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Full serializable config for the plugin.
|
|
32
|
+
*/
|
|
33
|
+
export type PluginCommandMenuConfig = {
|
|
34
|
+
/**
|
|
35
|
+
* Enable backdrop blur effect
|
|
36
|
+
* @default true
|
|
37
|
+
*/
|
|
38
|
+
blurBg?: boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Custom items or groups to add to the command menu.
|
|
41
|
+
*/
|
|
42
|
+
customItems?: (CustomMenuGroup | CustomMenuItem)[];
|
|
43
|
+
/**
|
|
44
|
+
* Disable the plugin functionality
|
|
45
|
+
* @default false
|
|
46
|
+
*/
|
|
47
|
+
disabled?: boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Custom icons for collections and globals.
|
|
50
|
+
* Key is the collection slug, value is the icon name from lucide-react.
|
|
51
|
+
* Collections default icon - Files,
|
|
52
|
+
* Globals default icon - Globe.
|
|
53
|
+
*/
|
|
54
|
+
icons?: {
|
|
55
|
+
/**
|
|
56
|
+
* Custom icons for collections.
|
|
57
|
+
* @default <Files/>
|
|
58
|
+
*/
|
|
59
|
+
collections?: {
|
|
60
|
+
[key: CollectionSlug]: IconName;
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* Custom icons for globals.
|
|
64
|
+
* @default <Globe/>
|
|
65
|
+
*/
|
|
66
|
+
globals?: {
|
|
67
|
+
[key: GlobalSlug]: IconName;
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
/**
|
|
71
|
+
* Configuration for the search button in the admin navigation.
|
|
72
|
+
* Set to false to disable the search button.
|
|
73
|
+
* @default { position: 'actions' }
|
|
74
|
+
*/
|
|
75
|
+
searchButton?: {
|
|
76
|
+
/**
|
|
77
|
+
* Position of the search button in the admin navigation.
|
|
78
|
+
* @default 'actions'
|
|
79
|
+
*/
|
|
80
|
+
position?: 'actions' | 'nav';
|
|
81
|
+
} | false;
|
|
82
|
+
/**
|
|
83
|
+
* Keyboard shortcut to open the command menu.
|
|
84
|
+
* Can be a single shortcut string or an array of shortcuts for cross-platform support.
|
|
85
|
+
* @default ['meta+k', 'ctrl+k']
|
|
86
|
+
* @example 'mod+k' or ['meta+k', 'ctrl+k']
|
|
87
|
+
*
|
|
88
|
+
* More details here - https://react-hotkeys-hook.vercel.app/docs/intro
|
|
89
|
+
*/
|
|
90
|
+
shortcut?: string | string[];
|
|
91
|
+
/**
|
|
92
|
+
* Specify which collections slugs remove from the command menu.
|
|
93
|
+
* @default ['payload-migrations','payload-preferences','payload-locked-documents']
|
|
94
|
+
*
|
|
95
|
+
* You can also provide an object with `ignoreList` and `replaceDefaults` properties.
|
|
96
|
+
* `replaceDefaults` allows you to completely replace the default slugs to ignore instead of appending to them.
|
|
97
|
+
*/
|
|
98
|
+
slugsToIgnore?: {
|
|
99
|
+
/**
|
|
100
|
+
* List of collection/global slugs to ignore in the command menu.
|
|
101
|
+
*/
|
|
102
|
+
ignoreList: CollectionSlug[];
|
|
103
|
+
/**
|
|
104
|
+
* Whether to replace the default slugs to ignore instead of appending to them.
|
|
105
|
+
*/
|
|
106
|
+
replaceDefaults?: boolean;
|
|
107
|
+
} | CollectionSlug[];
|
|
108
|
+
/**
|
|
109
|
+
* Configure submenu behavior for collections.
|
|
110
|
+
* When enabled, users can search within a collection's documents.
|
|
111
|
+
* @default { enabled: true, shortcut: 'shift+enter' }
|
|
112
|
+
*/
|
|
113
|
+
submenu?: {
|
|
114
|
+
/**
|
|
115
|
+
* Enable or disable submenu functionality.
|
|
116
|
+
* @default true
|
|
117
|
+
*/
|
|
118
|
+
enabled?: boolean;
|
|
119
|
+
/**
|
|
120
|
+
* Custom icons for collection submenus.
|
|
121
|
+
* Key is the collection slug, value is the icon name from lucide-react.
|
|
122
|
+
*
|
|
123
|
+
* @default null
|
|
124
|
+
*/
|
|
125
|
+
icons?: {
|
|
126
|
+
[key: CollectionSlug]: IconName;
|
|
127
|
+
};
|
|
128
|
+
/**
|
|
129
|
+
* Keyboard shortcut to open collection submenu.
|
|
130
|
+
* - 'shift+enter': Shift+Enter opens submenu, Enter navigates to collection list
|
|
131
|
+
* - 'enter': Enter opens submenu, Shift+Enter navigates to collection list
|
|
132
|
+
* @default 'shift+enter'
|
|
133
|
+
*/
|
|
134
|
+
shortcut?: 'enter' | 'shift+enter';
|
|
135
|
+
};
|
|
136
|
+
};
|
|
137
|
+
export interface CommandMenuContextProps {
|
|
138
|
+
children: React.ReactNode;
|
|
139
|
+
pluginConfig: PluginCommandMenuConfig;
|
|
140
|
+
}
|
|
141
|
+
export interface CommandMenuActionLink {
|
|
142
|
+
href: string;
|
|
143
|
+
type: 'link';
|
|
144
|
+
}
|
|
145
|
+
export interface CommandMenuActionAPICall {
|
|
146
|
+
body?: {
|
|
147
|
+
[key: string]: unknown;
|
|
148
|
+
};
|
|
149
|
+
href: string;
|
|
150
|
+
/**
|
|
151
|
+
* HTTP method to use for the API call.
|
|
152
|
+
* @default 'GET'
|
|
153
|
+
*/
|
|
154
|
+
method?: 'DELETE' | 'GET' | 'POST' | 'PUT';
|
|
155
|
+
type: 'api';
|
|
156
|
+
}
|
|
157
|
+
export type CommandMenuAction = CommandMenuActionAPICall | CommandMenuActionLink;
|
|
158
|
+
export interface CommandMenuItem {
|
|
159
|
+
/**
|
|
160
|
+
* Action to perform when the command menu item is selected.
|
|
161
|
+
*/
|
|
162
|
+
action: CommandMenuAction;
|
|
163
|
+
icon?: InternalIcon;
|
|
164
|
+
label: string;
|
|
165
|
+
slug: string;
|
|
166
|
+
/**
|
|
167
|
+
* Type of the command menu item. Used for grouping and icons.
|
|
168
|
+
* @default 'custom'
|
|
169
|
+
*/
|
|
170
|
+
type: 'collection' | 'custom' | 'global';
|
|
171
|
+
/**
|
|
172
|
+
* Field name used as title for collection documents.
|
|
173
|
+
* Only applicable for collection type items.
|
|
174
|
+
* Defaults to 'id' if not specified.
|
|
175
|
+
*/
|
|
176
|
+
useAsTitle?: string;
|
|
177
|
+
/**
|
|
178
|
+
* Label for the field used as title for collection documents.
|
|
179
|
+
* Only applicable for collection type items.
|
|
180
|
+
*
|
|
181
|
+
* Used in submenu search placeholder.
|
|
182
|
+
*/
|
|
183
|
+
useAsTitleLabel?: string;
|
|
184
|
+
}
|
|
185
|
+
export interface CommandMenuGroup {
|
|
186
|
+
items: CommandMenuItem[];
|
|
187
|
+
title: string;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Page state for command menu navigation.
|
|
191
|
+
* - 'main': Default view showing all collections/globals/custom items
|
|
192
|
+
* - CollectionSearchPage: Submenu view for searching within a specific collection
|
|
193
|
+
*/
|
|
194
|
+
export type CommandMenuPage = 'main' | {
|
|
195
|
+
/**
|
|
196
|
+
* Collection label for display
|
|
197
|
+
*/
|
|
198
|
+
label: string;
|
|
199
|
+
/**
|
|
200
|
+
* Collection slug
|
|
201
|
+
*/
|
|
202
|
+
slug: string;
|
|
203
|
+
/**
|
|
204
|
+
* Page type identifier
|
|
205
|
+
*/
|
|
206
|
+
type: 'collection-search';
|
|
207
|
+
/**
|
|
208
|
+
* Field name to use as document title
|
|
209
|
+
*/
|
|
210
|
+
useAsTitle: string;
|
|
211
|
+
/**
|
|
212
|
+
* Label for the field used as title
|
|
213
|
+
*/
|
|
214
|
+
useAsTitleLabel: string;
|
|
215
|
+
};
|
|
216
|
+
/**
|
|
217
|
+
* Generic document type for collections, with dynamic keys.
|
|
218
|
+
* We assume values are either string or number for simplicity, useAsTitle is making sure of that.
|
|
219
|
+
*/
|
|
220
|
+
export type GenericCollectionDocument = {
|
|
221
|
+
[key: string]: number | string;
|
|
222
|
+
id: string;
|
|
223
|
+
};
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/types.ts"],"sourcesContent":["import type { LucideIcon } from 'lucide-react'\nimport type { IconName } from 'lucide-react/dynamic'\nimport type { CollectionSlug, GlobalSlug } from 'payload'\n\nexport type LocalizedString = { [locale: string]: string } | string\n\nexport type InternalIcon = IconName | LucideIcon\n\n/**\n * Custom menu item, for configuration.\n * Will be mapped to CommandMenuItem internally.\n */\nexport type CustomMenuItem = {\n action: CommandMenuAction\n icon?: IconName\n label: LocalizedString\n slug: string\n type: 'item'\n}\n\n/**\n * Custom menu group, for configuration.\n * Will be mapped to CommandMenuGroup internally.\n *\n * Groups will be merged if they have the same title.\n */\nexport type CustomMenuGroup = {\n items: CustomMenuItem[]\n title: LocalizedString\n type: 'group'\n}\n/**\n * Full serializable config for the plugin.\n */\nexport type PluginCommandMenuConfig = {\n /**\n * Enable backdrop blur effect\n * @default true\n */\n blurBg?: boolean\n /**\n * Custom items or groups to add to the command menu.\n */\n customItems?: (CustomMenuGroup | CustomMenuItem)[]\n /**\n * Disable the plugin functionality\n * @default false\n */\n disabled?: boolean\n /**\n * Custom icons for collections and globals.\n * Key is the collection slug, value is the icon name from lucide-react.\n * Collections default icon - Files,\n * Globals default icon - Globe.\n */\n icons?: {\n /**\n * Custom icons for collections.\n * @default <Files/>\n */\n collections?: {\n [key: CollectionSlug]: IconName\n }\n /**\n * Custom icons for globals.\n * @default <Globe/>\n */\n globals?: {\n [key: GlobalSlug]: IconName\n }\n }\n /**\n * Configuration for the search button in the admin navigation.\n * Set to false to disable the search button.\n * @default { position: 'actions' }\n */\n searchButton?:\n | {\n /**\n * Position of the search button in the admin navigation.\n * @default 'actions'\n */\n position?: 'actions' | 'nav'\n }\n | false\n /**\n * Keyboard shortcut to open the command menu.\n * Can be a single shortcut string or an array of shortcuts for cross-platform support.\n * @default ['meta+k', 'ctrl+k']\n * @example 'mod+k' or ['meta+k', 'ctrl+k']\n *\n * More details here - https://react-hotkeys-hook.vercel.app/docs/intro\n */\n shortcut?: string | string[]\n /**\n * Specify which collections slugs remove from the command menu.\n * @default ['payload-migrations','payload-preferences','payload-locked-documents']\n *\n * You can also provide an object with `ignoreList` and `replaceDefaults` properties.\n * `replaceDefaults` allows you to completely replace the default slugs to ignore instead of appending to them.\n */\n slugsToIgnore?:\n | {\n /**\n * List of collection/global slugs to ignore in the command menu.\n */\n ignoreList: CollectionSlug[]\n /**\n * Whether to replace the default slugs to ignore instead of appending to them.\n */\n replaceDefaults?: boolean\n }\n | CollectionSlug[]\n /**\n * Configure submenu behavior for collections.\n * When enabled, users can search within a collection's documents.\n * @default { enabled: true, shortcut: 'shift+enter' }\n */\n submenu?: {\n /**\n * Enable or disable submenu functionality.\n * @default true\n */\n enabled?: boolean\n /**\n * Custom icons for collection submenus.\n * Key is the collection slug, value is the icon name from lucide-react.\n *\n * @default null\n */\n icons?: {\n [key: CollectionSlug]: IconName\n }\n /**\n * Keyboard shortcut to open collection submenu.\n * - 'shift+enter': Shift+Enter opens submenu, Enter navigates to collection list\n * - 'enter': Enter opens submenu, Shift+Enter navigates to collection list\n * @default 'shift+enter'\n */\n shortcut?: 'enter' | 'shift+enter'\n }\n}\n\nexport interface CommandMenuContextProps {\n children: React.ReactNode\n pluginConfig: PluginCommandMenuConfig\n}\n\nexport interface CommandMenuActionLink {\n href: string\n type: 'link'\n}\n\nexport interface CommandMenuActionAPICall {\n body?: {\n [key: string]: unknown\n }\n href: string\n /**\n * HTTP method to use for the API call.\n * @default 'GET'\n */\n method?: 'DELETE' | 'GET' | 'POST' | 'PUT'\n type: 'api'\n}\n\nexport type CommandMenuAction = CommandMenuActionAPICall | CommandMenuActionLink\n\nexport interface CommandMenuItem {\n /**\n * Action to perform when the command menu item is selected.\n */\n action: CommandMenuAction\n icon?: InternalIcon\n label: string\n slug: string\n /**\n * Type of the command menu item. Used for grouping and icons.\n * @default 'custom'\n */\n type: 'collection' | 'custom' | 'global'\n\n /**\n * Field name used as title for collection documents.\n * Only applicable for collection type items.\n * Defaults to 'id' if not specified.\n */\n useAsTitle?: string\n /**\n * Label for the field used as title for collection documents.\n * Only applicable for collection type items.\n *\n * Used in submenu search placeholder.\n */\n useAsTitleLabel?: string\n}\n\nexport interface CommandMenuGroup {\n items: CommandMenuItem[]\n title: string\n}\n\n/**\n * Page state for command menu navigation.\n * - 'main': Default view showing all collections/globals/custom items\n * - CollectionSearchPage: Submenu view for searching within a specific collection\n */\nexport type CommandMenuPage =\n | 'main'\n | {\n /**\n * Collection label for display\n */\n label: string\n /**\n * Collection slug\n */\n slug: string\n /**\n * Page type identifier\n */\n type: 'collection-search'\n /**\n * Field name to use as document title\n */\n useAsTitle: string\n /**\n * Label for the field used as title\n */\n useAsTitleLabel: string\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"],"names":[],"mappings":"AAwOA;;;CAGC,GACD,WAGC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { ClientConfig, LabelFunction } from 'payload';
|
|
2
|
+
import type { CommandMenuGroup, CommandMenuItem, CustomMenuGroup, CustomMenuItem, LocalizedString, PluginCommandMenuConfig } from 'src/types';
|
|
3
|
+
export declare const convertSlugToTitle: (slug: string) => string;
|
|
4
|
+
export declare const extractLocalizedValue: (value: LocalizedString, locale: string, slug?: string) => string;
|
|
5
|
+
export declare const extractLocalizedCollectionName: (collection: {
|
|
6
|
+
labels?: {
|
|
7
|
+
plural?: LocalizedString;
|
|
8
|
+
singular?: LocalizedString;
|
|
9
|
+
};
|
|
10
|
+
slug: string;
|
|
11
|
+
}, locale: string) => string;
|
|
12
|
+
export declare const extractLocalizedGlobalName: (global: {
|
|
13
|
+
label?: LabelFunction | LocalizedString;
|
|
14
|
+
slug: string;
|
|
15
|
+
}, locale: string) => string;
|
|
16
|
+
export declare const extractLocalizedGroupName: (object: {
|
|
17
|
+
admin?: {
|
|
18
|
+
group?: false | LocalizedString;
|
|
19
|
+
};
|
|
20
|
+
}, locale: string) => null | string;
|
|
21
|
+
export declare const convertConfigItem: (item: CustomMenuItem, currentLang: string) => CommandMenuItem;
|
|
22
|
+
export declare const convertConfigGroup: (group: CustomMenuGroup, currentLang: string) => CommandMenuGroup;
|
|
23
|
+
export declare const createDefaultGroups: (config: ClientConfig, currentLang: string, pluginConfig: PluginCommandMenuConfig) => {
|
|
24
|
+
groups: CommandMenuGroup[];
|
|
25
|
+
/**
|
|
26
|
+
* Stray items that don't belong to any group.
|
|
27
|
+
* Only with custom items.
|
|
28
|
+
*/
|
|
29
|
+
items: CommandMenuItem[];
|
|
30
|
+
};
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { Files, Globe } from 'lucide-react';
|
|
2
|
+
export const convertSlugToTitle = (slug)=>{
|
|
3
|
+
return slug.replace(/-/g, ' ').replace(/\b\w/g, (char)=>char.toUpperCase());
|
|
4
|
+
};
|
|
5
|
+
export const extractLocalizedValue = (value, locale, slug)=>{
|
|
6
|
+
if (typeof value === 'string') {
|
|
7
|
+
return value;
|
|
8
|
+
}
|
|
9
|
+
return value[locale] || convertSlugToTitle(slug || '');
|
|
10
|
+
};
|
|
11
|
+
export const extractLocalizedCollectionName = (collection, locale)=>{
|
|
12
|
+
//Get plural name if exists, otherwise singular, otherwise slug
|
|
13
|
+
if (collection.labels?.plural) {
|
|
14
|
+
return extractLocalizedValue(collection.labels.plural, locale, collection.slug);
|
|
15
|
+
}
|
|
16
|
+
if (collection.labels?.singular) {
|
|
17
|
+
return extractLocalizedValue(collection.labels.singular, locale, collection.slug);
|
|
18
|
+
}
|
|
19
|
+
return '' //Generally should not happen
|
|
20
|
+
;
|
|
21
|
+
};
|
|
22
|
+
export const extractLocalizedGlobalName = (global, locale)=>{
|
|
23
|
+
if (global.label) {
|
|
24
|
+
return extractLocalizedValue(//Ignore label functions, they are not serializable
|
|
25
|
+
typeof global.label === 'function' ? {} : global.label, locale, global.slug);
|
|
26
|
+
}
|
|
27
|
+
return '' //Generally should not happen
|
|
28
|
+
;
|
|
29
|
+
};
|
|
30
|
+
export const extractLocalizedGroupName = (object, locale)=>{
|
|
31
|
+
if (object.admin?.group) {
|
|
32
|
+
//Try to extract group name, with fallback to null (no group)
|
|
33
|
+
return extractLocalizedValue(object.admin.group, locale)?.trim() || null;
|
|
34
|
+
}
|
|
35
|
+
return null;
|
|
36
|
+
};
|
|
37
|
+
export const convertConfigItem = (item, currentLang)=>{
|
|
38
|
+
return {
|
|
39
|
+
slug: item.slug,
|
|
40
|
+
type: 'custom',
|
|
41
|
+
action: item.action,
|
|
42
|
+
icon: item.icon,
|
|
43
|
+
label: extractLocalizedValue(item.label, currentLang, item.slug)
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
export const convertConfigGroup = (group, currentLang)=>{
|
|
47
|
+
return {
|
|
48
|
+
items: group.items.map((item)=>convertConfigItem(item, currentLang)),
|
|
49
|
+
title: extractLocalizedValue(group.title, currentLang)
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* Set of collection/globals slugs to ignore in the command menu.
|
|
54
|
+
* This is useful to avoid showing certain collections/globals in the command menu.
|
|
55
|
+
*/ const DEFAULT_SLUGS_TO_IGNORE = [
|
|
56
|
+
'payload-migrations',
|
|
57
|
+
'payload-preferences',
|
|
58
|
+
'payload-locked-documents'
|
|
59
|
+
];
|
|
60
|
+
export const createDefaultGroups = (config, currentLang, pluginConfig)=>{
|
|
61
|
+
const groups = [];
|
|
62
|
+
const items = [];
|
|
63
|
+
const avaibleGroups = new Set() //To avoid duplicates
|
|
64
|
+
;
|
|
65
|
+
let slugsToIgnore = [
|
|
66
|
+
...DEFAULT_SLUGS_TO_IGNORE
|
|
67
|
+
];
|
|
68
|
+
//Handle slugs to ignore from plugin config
|
|
69
|
+
if (pluginConfig?.slugsToIgnore) {
|
|
70
|
+
if (Array.isArray(pluginConfig.slugsToIgnore)) {
|
|
71
|
+
slugsToIgnore.push(...pluginConfig.slugsToIgnore);
|
|
72
|
+
} else {
|
|
73
|
+
//Object with ignoreList and replaceDefaults
|
|
74
|
+
if (pluginConfig.slugsToIgnore.replaceDefaults) {
|
|
75
|
+
//Replace defaults
|
|
76
|
+
slugsToIgnore = []; //Reset
|
|
77
|
+
}
|
|
78
|
+
slugsToIgnore.push(...pluginConfig.slugsToIgnore.ignoreList);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if (config.collections) {
|
|
82
|
+
config.collections.forEach((collection)=>{
|
|
83
|
+
if (slugsToIgnore.includes(collection.slug)) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const groupName = extractLocalizedGroupName(collection, currentLang) || 'Collections';
|
|
87
|
+
// console.log(collection.slug, 'groupName:', groupName, 'Object', collection.admin)
|
|
88
|
+
if (!avaibleGroups.has(groupName)) {
|
|
89
|
+
avaibleGroups.add(groupName);
|
|
90
|
+
groups.push({
|
|
91
|
+
items: [],
|
|
92
|
+
title: groupName
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
const group = groups.find((g)=>g.title === groupName);
|
|
96
|
+
if (group) {
|
|
97
|
+
const useAsTitleField = collection.admin?.useAsTitle || 'id';
|
|
98
|
+
let useAsTitleFieldLabel = undefined;
|
|
99
|
+
let useAsTitleLabel = undefined;
|
|
100
|
+
//Only extract useAsTitle label if submenu is enabled
|
|
101
|
+
if (pluginConfig?.submenu?.enabled !== false) {
|
|
102
|
+
useAsTitleFieldLabel = collection?.fields?.find((field)=>{
|
|
103
|
+
if ('name' in field === false) {
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
return field.name === useAsTitleField;
|
|
107
|
+
})?.label;
|
|
108
|
+
//Extract label for useAsTitle field
|
|
109
|
+
useAsTitleLabel = extractLocalizedValue(typeof useAsTitleFieldLabel === 'function' ? {} : useAsTitleFieldLabel || {}, currentLang, useAsTitleField);
|
|
110
|
+
}
|
|
111
|
+
group.items.push({
|
|
112
|
+
slug: collection.slug,
|
|
113
|
+
type: 'collection',
|
|
114
|
+
action: {
|
|
115
|
+
type: 'link',
|
|
116
|
+
href: `/admin/collections/${collection.slug}`
|
|
117
|
+
},
|
|
118
|
+
//Either custom icon from plugin config, or default Files icon
|
|
119
|
+
icon: pluginConfig?.icons?.collections?.[collection.slug] || Files,
|
|
120
|
+
label: extractLocalizedCollectionName(collection, currentLang),
|
|
121
|
+
useAsTitle: useAsTitleField,
|
|
122
|
+
useAsTitleLabel: useAsTitleLabel || useAsTitleField
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
//Globals
|
|
128
|
+
if (config.globals) {
|
|
129
|
+
config.globals.forEach((global)=>{
|
|
130
|
+
if (slugsToIgnore.includes(global.slug)) {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
//Same logic as collections
|
|
134
|
+
const groupName = extractLocalizedGroupName(global, currentLang) || 'Globals';
|
|
135
|
+
if (!avaibleGroups.has(groupName)) {
|
|
136
|
+
avaibleGroups.add(groupName);
|
|
137
|
+
groups.push({
|
|
138
|
+
items: [],
|
|
139
|
+
title: groupName
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
const group = groups.find((g)=>g.title === groupName);
|
|
143
|
+
if (group) {
|
|
144
|
+
group.items.push({
|
|
145
|
+
slug: global.slug,
|
|
146
|
+
type: 'global',
|
|
147
|
+
action: {
|
|
148
|
+
type: 'link',
|
|
149
|
+
href: `/admin/globals/${global.slug}`
|
|
150
|
+
},
|
|
151
|
+
//Either custom icon from plugin config, or default Globe icon
|
|
152
|
+
icon: pluginConfig?.icons?.globals?.[global.slug] || Globe,
|
|
153
|
+
label: extractLocalizedGlobalName(global, currentLang)
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
//Now custom items/groups from plugin config
|
|
159
|
+
if (pluginConfig?.customItems) {
|
|
160
|
+
//We are not using slugsToIgnore for custom items, as they are user-defined
|
|
161
|
+
pluginConfig.customItems.forEach((value)=>{
|
|
162
|
+
if (value.type === 'group') {
|
|
163
|
+
const convertedGroup = convertConfigGroup(value, currentLang);
|
|
164
|
+
//Check if group already exists using our set
|
|
165
|
+
if (!avaibleGroups.has(convertedGroup.title)) {
|
|
166
|
+
avaibleGroups.add(convertedGroup.title);
|
|
167
|
+
groups.push({
|
|
168
|
+
items: [],
|
|
169
|
+
title: convertedGroup.title
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
const group = groups.find((g)=>g.title === convertedGroup.title);
|
|
173
|
+
if (group) {
|
|
174
|
+
//Append items to existing group, or if it was empty - add items
|
|
175
|
+
group.items.push(...convertedGroup.items);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
if (value.type === 'item') {
|
|
179
|
+
//Stray item, add to items array
|
|
180
|
+
const convertedItem = convertConfigItem(value, currentLang);
|
|
181
|
+
items.push(convertedItem);
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
return {
|
|
186
|
+
groups,
|
|
187
|
+
items
|
|
188
|
+
};
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
//# sourceMappingURL=index.js.map
|