feat(ui): adds admin.components.listMenuItems option (#11230)
### What? Adds new option `admin.components.listMenuItems` to allow custom components to be injected after the existing list controls in the collection list view. ### Why? Needed to facilitate import/export plugin. #### Testing Use `pnpm dev admin` to see example component and see test added to `test/admin/e2e/list-view`. ## Update since feature was reverted The custom list controls and now rendered with no surrounding padding or border radius. <img width="596" alt="Screenshot 2025-02-17 at 5 06 44 PM" src="https://github.com/user-attachments/assets/57209367-5433-4a4c-8797-0f9671da15c8" /> --------- Co-authored-by: Dan Ribbens <dan.ribbens@gmail.com>
This commit is contained in:
committed by
GitHub
parent
117949b8d9
commit
8a2b712287
@@ -152,15 +152,16 @@ export const MyCollection: CollectionConfig = {
|
|||||||
|
|
||||||
The following options are available:
|
The following options are available:
|
||||||
|
|
||||||
| Option | Description |
|
| Option | Description |
|
||||||
| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
| --------------------- |------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| `afterList` | An array of components to inject _after_ the built-in List View. [More details](../custom-components/list-view#afterlist). |
|
| `afterList` | An array of components to inject _after_ the built-in List View. [More details](../custom-components/list-view#afterlist). |
|
||||||
| `afterListTable` | An array of components to inject _after_ the built-in List View's table. [More details](../custom-components/list-view#afterlisttable). |
|
| `afterListTable` | An array of components to inject _after_ the built-in List View's table. [More details](../custom-components/list-view#afterlisttable). |
|
||||||
| `beforeList` | An array of components to inject _before_ the built-in List View. [More details](../custom-components/list-view#beforelist). |
|
| `beforeList` | An array of components to inject _before_ the built-in List View. [More details](../custom-components/list-view#beforelist). |
|
||||||
| `beforeListTable` | An array of components to inject _before_ the built-in List View's table. [More details](../custom-components/list-view#beforelisttable). |
|
| `beforeListTable` | An array of components to inject _before_ the built-in List View's table. [More details](../custom-components/list-view#beforelisttable). |
|
||||||
| `Description` | A component to render below the Collection label in the List View. An alternative to the `admin.description` property. [More details](../custom-components/list-view#description). |
|
| `listMenuItems` | An array of components to render within a menu next to the List Controls (after the Columns and Filters options) |
|
||||||
| `edit` | Override specific components within the Edit View. [More details](#edit-view-options). |
|
| `Description` | A component to render below the Collection label in the List View. An alternative to the `admin.description` property. [More details](../custom-components/list-view#description). |
|
||||||
| `views` | Override or create new views within the Admin Panel. [More details](../custom-components/custom-views). |
|
| `edit` | Override specific components within the Edit View. [More details](#edit-view-options). |
|
||||||
|
| `views` | Override or create new views within the Admin Panel. [More details](../custom-components/custom-views). |
|
||||||
|
|
||||||
#### Edit View Options
|
#### Edit View Options
|
||||||
|
|
||||||
|
|||||||
@@ -44,6 +44,18 @@ export const renderListViewSlots = ({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const listMenuItems = collectionConfig.admin.components?.listMenuItems
|
||||||
|
if (Array.isArray(listMenuItems)) {
|
||||||
|
result.listMenuItems = [
|
||||||
|
RenderServerComponent({
|
||||||
|
clientProps,
|
||||||
|
Component: listMenuItems,
|
||||||
|
importMap: payload.importMap,
|
||||||
|
serverProps,
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
if (collectionConfig.admin.components?.afterListTable) {
|
if (collectionConfig.admin.components?.afterListTable) {
|
||||||
result.AfterListTable = RenderServerComponent({
|
result.AfterListTable = RenderServerComponent({
|
||||||
clientProps: clientProps satisfies AfterListTableClientProps,
|
clientProps: clientProps satisfies AfterListTableClientProps,
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ export type ListViewSlots = {
|
|||||||
BeforeList?: React.ReactNode
|
BeforeList?: React.ReactNode
|
||||||
BeforeListTable?: React.ReactNode
|
BeforeListTable?: React.ReactNode
|
||||||
Description?: React.ReactNode
|
Description?: React.ReactNode
|
||||||
|
listMenuItems?: React.ReactNode[]
|
||||||
Table: React.ReactNode
|
Table: React.ReactNode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ export function iterateCollections({
|
|||||||
})
|
})
|
||||||
|
|
||||||
addToImportMap(collection.admin?.components?.afterList)
|
addToImportMap(collection.admin?.components?.afterList)
|
||||||
|
addToImportMap(collection.admin?.components?.listMenuItems)
|
||||||
addToImportMap(collection.admin?.components?.afterListTable)
|
addToImportMap(collection.admin?.components?.afterListTable)
|
||||||
addToImportMap(collection.admin?.components?.beforeList)
|
addToImportMap(collection.admin?.components?.beforeList)
|
||||||
addToImportMap(collection.admin?.components?.beforeListTable)
|
addToImportMap(collection.admin?.components?.beforeListTable)
|
||||||
|
|||||||
@@ -304,6 +304,7 @@ export type CollectionAdminOptions = {
|
|||||||
*/
|
*/
|
||||||
Upload?: CustomUpload
|
Upload?: CustomUpload
|
||||||
}
|
}
|
||||||
|
listMenuItems?: CustomComponent[]
|
||||||
views?: {
|
views?: {
|
||||||
/**
|
/**
|
||||||
* Set to a React component to replace the entire Edit View, including all nested routes.
|
* Set to a React component to replace the entire Edit View, including all nested routes.
|
||||||
|
|||||||
@@ -209,6 +209,7 @@ export const clientTranslationKeys = createClientTranslationKeys([
|
|||||||
'general:loading',
|
'general:loading',
|
||||||
'general:locale',
|
'general:locale',
|
||||||
'general:menu',
|
'general:menu',
|
||||||
|
'general:moreOptions',
|
||||||
'general:moveDown',
|
'general:moveDown',
|
||||||
'general:moveUp',
|
'general:moveUp',
|
||||||
'general:next',
|
'general:next',
|
||||||
|
|||||||
@@ -266,6 +266,7 @@ export const arTranslations: DefaultTranslationsObject = {
|
|||||||
locale: 'اللّغة',
|
locale: 'اللّغة',
|
||||||
locales: 'اللّغات',
|
locales: 'اللّغات',
|
||||||
menu: 'قائمة',
|
menu: 'قائمة',
|
||||||
|
moreOptions: 'خيارات أكثر',
|
||||||
moveDown: 'التّحريك إلى الأسفل',
|
moveDown: 'التّحريك إلى الأسفل',
|
||||||
moveUp: 'التّحريك إلى الأعلى',
|
moveUp: 'التّحريك إلى الأعلى',
|
||||||
newPassword: 'كلمة مرور جديدة',
|
newPassword: 'كلمة مرور جديدة',
|
||||||
|
|||||||
@@ -270,6 +270,7 @@ export const azTranslations: DefaultTranslationsObject = {
|
|||||||
locale: 'Lokal',
|
locale: 'Lokal',
|
||||||
locales: 'Dillər',
|
locales: 'Dillər',
|
||||||
menu: 'Menyu',
|
menu: 'Menyu',
|
||||||
|
moreOptions: 'Daha çox seçimlər',
|
||||||
moveDown: 'Aşağı hərəkət et',
|
moveDown: 'Aşağı hərəkət et',
|
||||||
moveUp: 'Yuxarı hərəkət et',
|
moveUp: 'Yuxarı hərəkət et',
|
||||||
newPassword: 'Yeni şifrə',
|
newPassword: 'Yeni şifrə',
|
||||||
|
|||||||
@@ -269,6 +269,7 @@ export const bgTranslations: DefaultTranslationsObject = {
|
|||||||
locale: 'Локализация',
|
locale: 'Локализация',
|
||||||
locales: 'Локализации',
|
locales: 'Локализации',
|
||||||
menu: 'Меню',
|
menu: 'Меню',
|
||||||
|
moreOptions: 'Повече опции',
|
||||||
moveDown: 'Надолу',
|
moveDown: 'Надолу',
|
||||||
moveUp: 'Нагоре',
|
moveUp: 'Нагоре',
|
||||||
newPassword: 'Нова парола',
|
newPassword: 'Нова парола',
|
||||||
|
|||||||
@@ -270,6 +270,7 @@ export const caTranslations: DefaultTranslationsObject = {
|
|||||||
locale: 'Idioma',
|
locale: 'Idioma',
|
||||||
locales: 'Idiomes',
|
locales: 'Idiomes',
|
||||||
menu: 'Menu',
|
menu: 'Menu',
|
||||||
|
moreOptions: 'Més opcions',
|
||||||
moveDown: 'Mou avall',
|
moveDown: 'Mou avall',
|
||||||
moveUp: 'Move amunt',
|
moveUp: 'Move amunt',
|
||||||
newPassword: 'Nova contrasenya',
|
newPassword: 'Nova contrasenya',
|
||||||
|
|||||||
@@ -268,6 +268,7 @@ export const csTranslations: DefaultTranslationsObject = {
|
|||||||
locale: 'Místní verze',
|
locale: 'Místní verze',
|
||||||
locales: 'Lokality',
|
locales: 'Lokality',
|
||||||
menu: 'Jídelní lístek',
|
menu: 'Jídelní lístek',
|
||||||
|
moreOptions: 'Více možností',
|
||||||
moveDown: 'Posunout dolů',
|
moveDown: 'Posunout dolů',
|
||||||
moveUp: 'Posunout nahoru',
|
moveUp: 'Posunout nahoru',
|
||||||
newPassword: 'Nové heslo',
|
newPassword: 'Nové heslo',
|
||||||
|
|||||||
@@ -268,6 +268,7 @@ export const daTranslations: DefaultTranslationsObject = {
|
|||||||
locale: 'Lokalitet',
|
locale: 'Lokalitet',
|
||||||
locales: 'Lokaliteter',
|
locales: 'Lokaliteter',
|
||||||
menu: 'Menu',
|
menu: 'Menu',
|
||||||
|
moreOptions: 'Flere muligheder',
|
||||||
moveDown: 'Ryk ned',
|
moveDown: 'Ryk ned',
|
||||||
moveUp: 'Ryk op',
|
moveUp: 'Ryk op',
|
||||||
newPassword: 'Ny adgangskode',
|
newPassword: 'Ny adgangskode',
|
||||||
|
|||||||
@@ -274,6 +274,7 @@ export const deTranslations: DefaultTranslationsObject = {
|
|||||||
locale: 'Sprache',
|
locale: 'Sprache',
|
||||||
locales: 'Sprachen',
|
locales: 'Sprachen',
|
||||||
menu: 'Menü',
|
menu: 'Menü',
|
||||||
|
moreOptions: 'Mehr Optionen',
|
||||||
moveDown: 'Nach unten bewegen',
|
moveDown: 'Nach unten bewegen',
|
||||||
moveUp: 'Nach oben bewegen',
|
moveUp: 'Nach oben bewegen',
|
||||||
newPassword: 'Neues Passwort',
|
newPassword: 'Neues Passwort',
|
||||||
|
|||||||
@@ -270,6 +270,7 @@ export const enTranslations = {
|
|||||||
locale: 'Locale',
|
locale: 'Locale',
|
||||||
locales: 'Locales',
|
locales: 'Locales',
|
||||||
menu: 'Menu',
|
menu: 'Menu',
|
||||||
|
moreOptions: 'More options',
|
||||||
moveDown: 'Move Down',
|
moveDown: 'Move Down',
|
||||||
moveUp: 'Move Up',
|
moveUp: 'Move Up',
|
||||||
newPassword: 'New Password',
|
newPassword: 'New Password',
|
||||||
|
|||||||
@@ -274,6 +274,7 @@ export const esTranslations: DefaultTranslationsObject = {
|
|||||||
locale: 'Regional',
|
locale: 'Regional',
|
||||||
locales: 'Locales',
|
locales: 'Locales',
|
||||||
menu: 'Menú',
|
menu: 'Menú',
|
||||||
|
moreOptions: 'Más opciones',
|
||||||
moveDown: 'Mover abajo',
|
moveDown: 'Mover abajo',
|
||||||
moveUp: 'Mover arriba',
|
moveUp: 'Mover arriba',
|
||||||
newPassword: 'Nueva contraseña',
|
newPassword: 'Nueva contraseña',
|
||||||
|
|||||||
@@ -267,6 +267,7 @@ export const etTranslations: DefaultTranslationsObject = {
|
|||||||
locale: 'Keel',
|
locale: 'Keel',
|
||||||
locales: 'Keeled',
|
locales: 'Keeled',
|
||||||
menu: 'Menüü',
|
menu: 'Menüü',
|
||||||
|
moreOptions: 'Rohkem valikuid',
|
||||||
moveDown: 'Liiguta alla',
|
moveDown: 'Liiguta alla',
|
||||||
moveUp: 'Liiguta üles',
|
moveUp: 'Liiguta üles',
|
||||||
newPassword: 'Uus parool',
|
newPassword: 'Uus parool',
|
||||||
|
|||||||
@@ -268,6 +268,7 @@ export const faTranslations: DefaultTranslationsObject = {
|
|||||||
locale: 'زبان',
|
locale: 'زبان',
|
||||||
locales: 'زبانها',
|
locales: 'زبانها',
|
||||||
menu: 'منو',
|
menu: 'منو',
|
||||||
|
moreOptions: 'گزینههای بیشتر',
|
||||||
moveDown: 'حرکت به پایین',
|
moveDown: 'حرکت به پایین',
|
||||||
moveUp: 'حرکت به بالا',
|
moveUp: 'حرکت به بالا',
|
||||||
newPassword: 'گذرواژه تازه',
|
newPassword: 'گذرواژه تازه',
|
||||||
|
|||||||
@@ -277,6 +277,7 @@ export const frTranslations: DefaultTranslationsObject = {
|
|||||||
locale: 'Paramètres régionaux',
|
locale: 'Paramètres régionaux',
|
||||||
locales: 'Paramètres régionaux',
|
locales: 'Paramètres régionaux',
|
||||||
menu: 'Menu',
|
menu: 'Menu',
|
||||||
|
moreOptions: "Plus d'options",
|
||||||
moveDown: 'Déplacer vers le bas',
|
moveDown: 'Déplacer vers le bas',
|
||||||
moveUp: 'Déplacer vers le haut',
|
moveUp: 'Déplacer vers le haut',
|
||||||
newPassword: 'Nouveau mot de passe',
|
newPassword: 'Nouveau mot de passe',
|
||||||
|
|||||||
@@ -264,6 +264,7 @@ export const heTranslations: DefaultTranslationsObject = {
|
|||||||
locale: 'שפה',
|
locale: 'שפה',
|
||||||
locales: 'שפות',
|
locales: 'שפות',
|
||||||
menu: 'תפריט',
|
menu: 'תפריט',
|
||||||
|
moreOptions: 'אפשרויות נוספות',
|
||||||
moveDown: 'הזז למטה',
|
moveDown: 'הזז למטה',
|
||||||
moveUp: 'הזז למעלה',
|
moveUp: 'הזז למעלה',
|
||||||
newPassword: 'סיסמה חדשה',
|
newPassword: 'סיסמה חדשה',
|
||||||
|
|||||||
@@ -270,6 +270,7 @@ export const hrTranslations: DefaultTranslationsObject = {
|
|||||||
locale: 'Jezik',
|
locale: 'Jezik',
|
||||||
locales: 'Prijevodi',
|
locales: 'Prijevodi',
|
||||||
menu: 'Izbornik',
|
menu: 'Izbornik',
|
||||||
|
moreOptions: 'Više opcija',
|
||||||
moveDown: 'Pomakni dolje',
|
moveDown: 'Pomakni dolje',
|
||||||
moveUp: 'Pomakni gore',
|
moveUp: 'Pomakni gore',
|
||||||
newPassword: 'Nova lozinka',
|
newPassword: 'Nova lozinka',
|
||||||
|
|||||||
@@ -272,6 +272,7 @@ export const huTranslations: DefaultTranslationsObject = {
|
|||||||
locale: 'Nyelv',
|
locale: 'Nyelv',
|
||||||
locales: 'Nyelvek',
|
locales: 'Nyelvek',
|
||||||
menu: 'Menü',
|
menu: 'Menü',
|
||||||
|
moreOptions: 'Több opció',
|
||||||
moveDown: 'Mozgatás lefelé',
|
moveDown: 'Mozgatás lefelé',
|
||||||
moveUp: 'Mozgatás felfelé',
|
moveUp: 'Mozgatás felfelé',
|
||||||
newPassword: 'Új jelszó',
|
newPassword: 'Új jelszó',
|
||||||
|
|||||||
@@ -273,6 +273,7 @@ export const itTranslations: DefaultTranslationsObject = {
|
|||||||
locale: 'Locale',
|
locale: 'Locale',
|
||||||
locales: 'Localizzazioni',
|
locales: 'Localizzazioni',
|
||||||
menu: 'Menù',
|
menu: 'Menù',
|
||||||
|
moreOptions: 'Più opzioni',
|
||||||
moveDown: 'Sposta sotto',
|
moveDown: 'Sposta sotto',
|
||||||
moveUp: 'Sposta sopra',
|
moveUp: 'Sposta sopra',
|
||||||
newPassword: 'Nuova Password',
|
newPassword: 'Nuova Password',
|
||||||
|
|||||||
@@ -270,6 +270,7 @@ export const jaTranslations: DefaultTranslationsObject = {
|
|||||||
locale: 'ロケール',
|
locale: 'ロケール',
|
||||||
locales: 'ロケール',
|
locales: 'ロケール',
|
||||||
menu: 'メニュー',
|
menu: 'メニュー',
|
||||||
|
moreOptions: 'より多くのオプション',
|
||||||
moveDown: '下へ移動',
|
moveDown: '下へ移動',
|
||||||
moveUp: '上へ移動',
|
moveUp: '上へ移動',
|
||||||
newPassword: '新しいパスワード',
|
newPassword: '新しいパスワード',
|
||||||
|
|||||||
@@ -268,6 +268,7 @@ export const koTranslations: DefaultTranslationsObject = {
|
|||||||
locale: 'locale',
|
locale: 'locale',
|
||||||
locales: 'locale',
|
locales: 'locale',
|
||||||
menu: '메뉴',
|
menu: '메뉴',
|
||||||
|
moreOptions: '더 많은 옵션',
|
||||||
moveDown: '아래로 이동',
|
moveDown: '아래로 이동',
|
||||||
moveUp: '위로 이동',
|
moveUp: '위로 이동',
|
||||||
newPassword: '새 비밀번호',
|
newPassword: '새 비밀번호',
|
||||||
|
|||||||
@@ -272,6 +272,7 @@ export const myTranslations: DefaultTranslationsObject = {
|
|||||||
locale: 'ဒေသ',
|
locale: 'ဒေသ',
|
||||||
locales: 'Locales',
|
locales: 'Locales',
|
||||||
menu: 'မီနူး',
|
menu: 'မီနူး',
|
||||||
|
moreOptions: 'ပိုမိုများစွာရွေးချယ်ခွင့်',
|
||||||
moveDown: 'Move Down',
|
moveDown: 'Move Down',
|
||||||
moveUp: 'Move Up',
|
moveUp: 'Move Up',
|
||||||
newPassword: 'စကားဝှက် အသစ်',
|
newPassword: 'စကားဝှက် အသစ်',
|
||||||
|
|||||||
@@ -270,6 +270,7 @@ export const nbTranslations: DefaultTranslationsObject = {
|
|||||||
locale: 'Lokalitet',
|
locale: 'Lokalitet',
|
||||||
locales: 'Språk',
|
locales: 'Språk',
|
||||||
menu: 'Meny',
|
menu: 'Meny',
|
||||||
|
moreOptions: 'Flere alternativer',
|
||||||
moveDown: 'Flytt ned',
|
moveDown: 'Flytt ned',
|
||||||
moveUp: 'Flytt opp',
|
moveUp: 'Flytt opp',
|
||||||
newPassword: 'Nytt passord',
|
newPassword: 'Nytt passord',
|
||||||
|
|||||||
@@ -273,6 +273,7 @@ export const nlTranslations: DefaultTranslationsObject = {
|
|||||||
locale: 'Taal',
|
locale: 'Taal',
|
||||||
locales: 'Landinstellingen',
|
locales: 'Landinstellingen',
|
||||||
menu: 'Menu',
|
menu: 'Menu',
|
||||||
|
moreOptions: 'Meer opties',
|
||||||
moveDown: 'Verplaats naar beneden',
|
moveDown: 'Verplaats naar beneden',
|
||||||
moveUp: 'Verplaats naar boven',
|
moveUp: 'Verplaats naar boven',
|
||||||
newPassword: 'Nieuw wachtwoord',
|
newPassword: 'Nieuw wachtwoord',
|
||||||
|
|||||||
@@ -270,6 +270,7 @@ export const plTranslations: DefaultTranslationsObject = {
|
|||||||
locale: 'Ustawienia regionalne',
|
locale: 'Ustawienia regionalne',
|
||||||
locales: 'Ustawienia regionalne',
|
locales: 'Ustawienia regionalne',
|
||||||
menu: 'Menu',
|
menu: 'Menu',
|
||||||
|
moreOptions: 'Więcej opcji',
|
||||||
moveDown: 'Przesuń niżej',
|
moveDown: 'Przesuń niżej',
|
||||||
moveUp: 'Przesuń wyżej',
|
moveUp: 'Przesuń wyżej',
|
||||||
newPassword: 'Nowe hasło',
|
newPassword: 'Nowe hasło',
|
||||||
|
|||||||
@@ -270,6 +270,7 @@ export const ptTranslations: DefaultTranslationsObject = {
|
|||||||
locale: 'Local',
|
locale: 'Local',
|
||||||
locales: 'Localizações',
|
locales: 'Localizações',
|
||||||
menu: 'Cardápio',
|
menu: 'Cardápio',
|
||||||
|
moreOptions: 'Mais opções',
|
||||||
moveDown: 'Mover para Baixo',
|
moveDown: 'Mover para Baixo',
|
||||||
moveUp: 'Mover para Cima',
|
moveUp: 'Mover para Cima',
|
||||||
newPassword: 'Nova Senha',
|
newPassword: 'Nova Senha',
|
||||||
|
|||||||
@@ -274,6 +274,7 @@ export const roTranslations: DefaultTranslationsObject = {
|
|||||||
locale: 'Localitate',
|
locale: 'Localitate',
|
||||||
locales: 'Localuri',
|
locales: 'Localuri',
|
||||||
menu: 'Meniu',
|
menu: 'Meniu',
|
||||||
|
moreOptions: 'Mai multe opțiuni',
|
||||||
moveDown: 'Mutați în jos',
|
moveDown: 'Mutați în jos',
|
||||||
moveUp: 'Mutați în sus',
|
moveUp: 'Mutați în sus',
|
||||||
newPassword: 'Parolă nouă',
|
newPassword: 'Parolă nouă',
|
||||||
|
|||||||
@@ -270,6 +270,7 @@ export const rsTranslations: DefaultTranslationsObject = {
|
|||||||
locale: 'Језик',
|
locale: 'Језик',
|
||||||
locales: 'Преводи',
|
locales: 'Преводи',
|
||||||
menu: 'Мени',
|
menu: 'Мени',
|
||||||
|
moreOptions: 'Više opcija',
|
||||||
moveDown: 'Помери доле',
|
moveDown: 'Помери доле',
|
||||||
moveUp: 'Помери горе',
|
moveUp: 'Помери горе',
|
||||||
newPassword: 'Нова лозинка',
|
newPassword: 'Нова лозинка',
|
||||||
|
|||||||
@@ -270,6 +270,7 @@ export const rsLatinTranslations: DefaultTranslationsObject = {
|
|||||||
locale: 'Jezik',
|
locale: 'Jezik',
|
||||||
locales: 'Prevodi',
|
locales: 'Prevodi',
|
||||||
menu: 'Meni',
|
menu: 'Meni',
|
||||||
|
moreOptions: 'Više opcija',
|
||||||
moveDown: 'Pomeri dole',
|
moveDown: 'Pomeri dole',
|
||||||
moveUp: 'Pomeri gore',
|
moveUp: 'Pomeri gore',
|
||||||
newPassword: 'Nova lozinka',
|
newPassword: 'Nova lozinka',
|
||||||
|
|||||||
@@ -272,6 +272,7 @@ export const ruTranslations: DefaultTranslationsObject = {
|
|||||||
locale: 'Локаль',
|
locale: 'Локаль',
|
||||||
locales: 'Локали',
|
locales: 'Локали',
|
||||||
menu: 'Меню',
|
menu: 'Меню',
|
||||||
|
moreOptions: 'Больше вариантов',
|
||||||
moveDown: 'Сдвинуть вниз',
|
moveDown: 'Сдвинуть вниз',
|
||||||
moveUp: 'Сдвинуть вверх',
|
moveUp: 'Сдвинуть вверх',
|
||||||
newPassword: 'Новый пароль',
|
newPassword: 'Новый пароль',
|
||||||
|
|||||||
@@ -271,6 +271,7 @@ export const skTranslations: DefaultTranslationsObject = {
|
|||||||
locale: 'Jazyk',
|
locale: 'Jazyk',
|
||||||
locales: 'Jazyky',
|
locales: 'Jazyky',
|
||||||
menu: 'Menu',
|
menu: 'Menu',
|
||||||
|
moreOptions: 'Viac možností',
|
||||||
moveDown: 'Presunúť dolu',
|
moveDown: 'Presunúť dolu',
|
||||||
moveUp: 'Presunúť hore',
|
moveUp: 'Presunúť hore',
|
||||||
newPassword: 'Nové heslo',
|
newPassword: 'Nové heslo',
|
||||||
|
|||||||
@@ -269,6 +269,7 @@ export const slTranslations: DefaultTranslationsObject = {
|
|||||||
locale: 'Jezik',
|
locale: 'Jezik',
|
||||||
locales: 'Jeziki',
|
locales: 'Jeziki',
|
||||||
menu: 'Meni',
|
menu: 'Meni',
|
||||||
|
moreOptions: 'Več možnosti',
|
||||||
moveDown: 'Premakni dol',
|
moveDown: 'Premakni dol',
|
||||||
moveUp: 'Premakni gor',
|
moveUp: 'Premakni gor',
|
||||||
newPassword: 'Novo geslo',
|
newPassword: 'Novo geslo',
|
||||||
|
|||||||
@@ -270,6 +270,7 @@ export const svTranslations: DefaultTranslationsObject = {
|
|||||||
locale: 'Lokal',
|
locale: 'Lokal',
|
||||||
locales: 'Språk',
|
locales: 'Språk',
|
||||||
menu: 'Meny',
|
menu: 'Meny',
|
||||||
|
moreOptions: 'Fler alternativ',
|
||||||
moveDown: 'Flytta Ner',
|
moveDown: 'Flytta Ner',
|
||||||
moveUp: 'Flytta Upp',
|
moveUp: 'Flytta Upp',
|
||||||
newPassword: 'Nytt Lösenord',
|
newPassword: 'Nytt Lösenord',
|
||||||
|
|||||||
@@ -266,6 +266,7 @@ export const thTranslations: DefaultTranslationsObject = {
|
|||||||
locale: 'ตำแหน่งที่ตั้ง',
|
locale: 'ตำแหน่งที่ตั้ง',
|
||||||
locales: 'ภาษา',
|
locales: 'ภาษา',
|
||||||
menu: 'เมนู',
|
menu: 'เมนู',
|
||||||
|
moreOptions: 'ตัวเลือกเพิ่มเติม',
|
||||||
moveDown: 'ขยับขึ้น',
|
moveDown: 'ขยับขึ้น',
|
||||||
moveUp: 'ขยับลง',
|
moveUp: 'ขยับลง',
|
||||||
newPassword: 'รหัสผ่านใหม่',
|
newPassword: 'รหัสผ่านใหม่',
|
||||||
|
|||||||
@@ -273,6 +273,7 @@ export const trTranslations: DefaultTranslationsObject = {
|
|||||||
locale: 'Yerel ayar',
|
locale: 'Yerel ayar',
|
||||||
locales: 'Diller',
|
locales: 'Diller',
|
||||||
menu: 'Menü',
|
menu: 'Menü',
|
||||||
|
moreOptions: 'Daha fazla seçenek',
|
||||||
moveDown: 'Aşağı taşı',
|
moveDown: 'Aşağı taşı',
|
||||||
moveUp: 'Yukarı taşı',
|
moveUp: 'Yukarı taşı',
|
||||||
newPassword: 'Yeni parola',
|
newPassword: 'Yeni parola',
|
||||||
|
|||||||
@@ -269,6 +269,7 @@ export const ukTranslations: DefaultTranslationsObject = {
|
|||||||
locale: 'Локаль',
|
locale: 'Локаль',
|
||||||
locales: 'Локалі',
|
locales: 'Локалі',
|
||||||
menu: 'Меню',
|
menu: 'Меню',
|
||||||
|
moreOptions: 'Більше варіантів',
|
||||||
moveDown: 'Перемістити нижче',
|
moveDown: 'Перемістити нижче',
|
||||||
moveUp: 'Перемістити вище',
|
moveUp: 'Перемістити вище',
|
||||||
newPassword: 'Новий пароль',
|
newPassword: 'Новий пароль',
|
||||||
|
|||||||
@@ -269,6 +269,7 @@ export const viTranslations: DefaultTranslationsObject = {
|
|||||||
locale: 'Ngôn ngữ',
|
locale: 'Ngôn ngữ',
|
||||||
locales: 'Khu vực',
|
locales: 'Khu vực',
|
||||||
menu: 'Thực đơn',
|
menu: 'Thực đơn',
|
||||||
|
moreOptions: 'Nhiều lựa chọn hơn',
|
||||||
moveDown: 'Di chuyển xuống',
|
moveDown: 'Di chuyển xuống',
|
||||||
moveUp: 'Di chuyển lên',
|
moveUp: 'Di chuyển lên',
|
||||||
newPassword: 'Mật khảu mới',
|
newPassword: 'Mật khảu mới',
|
||||||
|
|||||||
@@ -260,6 +260,7 @@ export const zhTranslations: DefaultTranslationsObject = {
|
|||||||
locale: '语言环境',
|
locale: '语言环境',
|
||||||
locales: '语言环境',
|
locales: '语言环境',
|
||||||
menu: '菜单',
|
menu: '菜单',
|
||||||
|
moreOptions: '更多选项',
|
||||||
moveDown: '向下移动',
|
moveDown: '向下移动',
|
||||||
moveUp: '向上移动',
|
moveUp: '向上移动',
|
||||||
newPassword: '新密码',
|
newPassword: '新密码',
|
||||||
|
|||||||
@@ -260,6 +260,7 @@ export const zhTwTranslations: DefaultTranslationsObject = {
|
|||||||
locale: '語言環境',
|
locale: '語言環境',
|
||||||
locales: '語言環境',
|
locales: '語言環境',
|
||||||
menu: '菜單',
|
menu: '菜單',
|
||||||
|
moreOptions: '更多選項',
|
||||||
moveDown: '向下移動',
|
moveDown: '向下移動',
|
||||||
moveUp: '向上移動',
|
moveUp: '向上移動',
|
||||||
newPassword: '新密碼',
|
newPassword: '新密碼',
|
||||||
|
|||||||
@@ -19,6 +19,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__custom-control {
|
||||||
|
padding: 0;
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
&__buttons-wrap {
|
&__buttons-wrap {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
@@ -5,8 +5,10 @@ import { useWindowInfo } from '@faceless-ui/window-info'
|
|||||||
import { getTranslation } from '@payloadcms/translations'
|
import { getTranslation } from '@payloadcms/translations'
|
||||||
import React, { Fragment, useEffect, useRef, useState } from 'react'
|
import React, { Fragment, useEffect, useRef, useState } from 'react'
|
||||||
|
|
||||||
|
import { Popup, PopupList } from '../../elements/Popup/index.js'
|
||||||
import { useUseTitleField } from '../../hooks/useUseAsTitle.js'
|
import { useUseTitleField } from '../../hooks/useUseAsTitle.js'
|
||||||
import { ChevronIcon } from '../../icons/Chevron/index.js'
|
import { ChevronIcon } from '../../icons/Chevron/index.js'
|
||||||
|
import { Dots } from '../../icons/Dots/index.js'
|
||||||
import { SearchIcon } from '../../icons/Search/index.js'
|
import { SearchIcon } from '../../icons/Search/index.js'
|
||||||
import { useListQuery } from '../../providers/ListQuery/index.js'
|
import { useListQuery } from '../../providers/ListQuery/index.js'
|
||||||
import { useTranslation } from '../../providers/Translation/index.js'
|
import { useTranslation } from '../../providers/Translation/index.js'
|
||||||
@@ -36,6 +38,7 @@ export type ListControlsProps = {
|
|||||||
readonly handleSearchChange?: (search: string) => void
|
readonly handleSearchChange?: (search: string) => void
|
||||||
readonly handleSortChange?: (sort: string) => void
|
readonly handleSortChange?: (sort: string) => void
|
||||||
readonly handleWhereChange?: (where: Where) => void
|
readonly handleWhereChange?: (where: Where) => void
|
||||||
|
readonly listMenuItems?: React.ReactNode[]
|
||||||
readonly renderedFilters?: Map<string, React.ReactNode>
|
readonly renderedFilters?: Map<string, React.ReactNode>
|
||||||
readonly resolvedFilterOptions?: Map<string, ResolvedFilterOptions>
|
readonly resolvedFilterOptions?: Map<string, ResolvedFilterOptions>
|
||||||
}
|
}
|
||||||
@@ -54,10 +57,10 @@ export const ListControls: React.FC<ListControlsProps> = (props) => {
|
|||||||
disableBulkEdit,
|
disableBulkEdit,
|
||||||
enableColumns = true,
|
enableColumns = true,
|
||||||
enableSort = false,
|
enableSort = false,
|
||||||
|
listMenuItems,
|
||||||
renderedFilters,
|
renderedFilters,
|
||||||
resolvedFilterOptions,
|
resolvedFilterOptions,
|
||||||
} = props
|
} = props
|
||||||
|
|
||||||
const { handleSearchChange, query } = useListQuery()
|
const { handleSearchChange, query } = useListQuery()
|
||||||
const titleField = useUseTitleField(collectionConfig)
|
const titleField = useUseTitleField(collectionConfig)
|
||||||
const { i18n, t } = useTranslation()
|
const { i18n, t } = useTranslation()
|
||||||
@@ -194,6 +197,17 @@ export const ListControls: React.FC<ListControlsProps> = (props) => {
|
|||||||
{t('general:sort')}
|
{t('general:sort')}
|
||||||
</Pill>
|
</Pill>
|
||||||
)}
|
)}
|
||||||
|
{listMenuItems && (
|
||||||
|
<Popup
|
||||||
|
button={<Dots ariaLabel={t('general:moreOptions')} />}
|
||||||
|
className={`${baseClass}__popup`}
|
||||||
|
horizontalAlign="right"
|
||||||
|
size="large"
|
||||||
|
verticalAlign="bottom"
|
||||||
|
>
|
||||||
|
<PopupList.ButtonGroup>{listMenuItems.map((item) => item)}</PopupList.ButtonGroup>
|
||||||
|
</Popup>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
27
packages/ui/src/icons/Dots/index.scss
Normal file
27
packages/ui/src/icons/Dots/index.scss
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
@import '../../scss/styles';
|
||||||
|
|
||||||
|
@layer payload-default {
|
||||||
|
.dots {
|
||||||
|
margin: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 2px;
|
||||||
|
background-color: var(--theme-elevation-150);
|
||||||
|
border-radius: $style-radius-m;
|
||||||
|
height: calc(var(--base) * 1.2);
|
||||||
|
width: calc(var(--base) * 1.2);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--theme-elevation-100);
|
||||||
|
}
|
||||||
|
|
||||||
|
> div {
|
||||||
|
width: 2.5px;
|
||||||
|
height: 2.5px;
|
||||||
|
border-radius: 100%;
|
||||||
|
background-color: currentColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
17
packages/ui/src/icons/Dots/index.tsx
Normal file
17
packages/ui/src/icons/Dots/index.tsx
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
import './index.scss'
|
||||||
|
|
||||||
|
export const Dots: React.FC<{ ariaLabel?: string; className?: string }> = ({
|
||||||
|
ariaLabel,
|
||||||
|
className,
|
||||||
|
}) => (
|
||||||
|
<div
|
||||||
|
aria-label={ariaLabel}
|
||||||
|
className={[className && className, 'dots'].filter(Boolean).join(' ')}
|
||||||
|
>
|
||||||
|
<div />
|
||||||
|
<div />
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
@@ -52,6 +52,7 @@ export function DefaultListView(props: ListViewClientProps) {
|
|||||||
disableBulkEdit,
|
disableBulkEdit,
|
||||||
enableRowSelections,
|
enableRowSelections,
|
||||||
hasCreatePermission,
|
hasCreatePermission,
|
||||||
|
listMenuItems,
|
||||||
listPreferences,
|
listPreferences,
|
||||||
newDocumentURL,
|
newDocumentURL,
|
||||||
preferenceKey,
|
preferenceKey,
|
||||||
@@ -195,6 +196,7 @@ export function DefaultListView(props: ListViewClientProps) {
|
|||||||
collectionSlug={collectionSlug}
|
collectionSlug={collectionSlug}
|
||||||
disableBulkDelete={disableBulkDelete}
|
disableBulkDelete={disableBulkDelete}
|
||||||
disableBulkEdit={disableBulkEdit}
|
disableBulkEdit={disableBulkEdit}
|
||||||
|
listMenuItems={listMenuItems}
|
||||||
renderedFilters={renderedFilters}
|
renderedFilters={renderedFilters}
|
||||||
resolvedFilterOptions={resolvedFilterOptions}
|
resolvedFilterOptions={resolvedFilterOptions}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -33,6 +33,26 @@ export const Posts: CollectionConfig = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
listMenuItems: [
|
||||||
|
{
|
||||||
|
path: '/components/Banner/index.js#Banner',
|
||||||
|
clientProps: {
|
||||||
|
message: 'listMenuItems',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/components/Banner/index.js#Banner',
|
||||||
|
clientProps: {
|
||||||
|
message: 'Many of them',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/components/Banner/index.js#Banner',
|
||||||
|
clientProps: {
|
||||||
|
message: 'Ok last one',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
afterList: [
|
afterList: [
|
||||||
{
|
{
|
||||||
path: '/components/Banner/index.js#Banner',
|
path: '/components/Banner/index.js#Banner',
|
||||||
|
|||||||
@@ -198,6 +198,19 @@ describe('List View', () => {
|
|||||||
).toBeVisible()
|
).toBeVisible()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('should render custom listMenuItems component', async () => {
|
||||||
|
await page.goto(postsUrl.list)
|
||||||
|
const kebabMenu = page.locator('.list-controls__popup')
|
||||||
|
await expect(kebabMenu).toBeVisible()
|
||||||
|
await kebabMenu.click()
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
page.locator('.popup-button-list').locator('div', {
|
||||||
|
hasText: 'listMenuItems',
|
||||||
|
}),
|
||||||
|
).toBeVisible()
|
||||||
|
})
|
||||||
|
|
||||||
test('should render custom afterListTable component', async () => {
|
test('should render custom afterListTable component', async () => {
|
||||||
await page.goto(postsUrl.list)
|
await page.goto(postsUrl.list)
|
||||||
await expect(
|
await expect(
|
||||||
|
|||||||
Reference in New Issue
Block a user